關於字節(內存)對齊

吐槽

最近又去面試工作機會,筆者是獨立遊戲開發者,一直是傾向於多面手的角色(着重遊戲開發全過程,包括了美術,即工程方面的,筆者可是懷揣着拯救國內這個悲催的遊戲市場的夢想呢)。然並卵,面試官從不問我的項目經驗,因此在優化和算法上吃了不少虧,真的是應了“面試造航母,工作擰螺絲”這句話,不過,算法確實很重要,這應該也就是他們爲啥樂此不疲的利用這個來篩選面試者的原因吧。 扯遠了,還是先記下他們常提的“字節對齊”吧,謀生重要啊。

原理

在特定平臺特定讀取模式下,cpu並不會隨機讀取內存,比如讀取結構體數據時,會以結構體首地址作爲參考點,位移x*n(n爲處理字長,這個跟編譯指令裏設置字節對齊值不是一個概念),故一但出現讀取奇數地址且長度大於1的數據時,就可能會出現讀取一個數據會讀取多次的現象,通常,編譯器會對結構體的每個字段進行字節對齊,使其達到n,一次即可讀取,從而提高cpu讀取效率。

優缺點

優點:

  1. 提高讀取特定數據效率
  2. 運用得當,在默認情況下能節省空間

缺點:

  1. 佔用額外無用空間,這也是空間換時間的弊端

應用場景

編譯器所做的字節對齊優化,在特定場景中,我們並不期望有。比如在網絡協議模塊,由於這些協議字段經常會有變動,但如果按照默認對齊方式的話,程序修改會很麻煩。比如客戶端和服務端的對接會遇到問題,不同平臺很難確定讀取或是寫入的地址。因此,通常服務協議的結構體都不按默認對齊方式,你會看到有:

#pragma pack(push,1) 
...
#pragma pack(pop)

這一對編譯指令,這樣修改協議會簡單很多。那爲什麼默認對齊字節不是1而是4呢?這樣不僅省空間,還能解決一些麻煩的問題。筆者在原理中已經提到了,在特定平臺特定的讀取模式下,這樣可能會降低cpu讀取內存的效率。

例子

先介紹三個概念:自身對齊值、指定對齊值、有效對齊值。
自身對齊值:數據類型本身的對齊值,例如char類型的自身對齊值是1,short類型是2;
指定對齊值:編譯器或程序員指定的對齊值,32位單片機的指定對齊值默認是4;
有效對齊值:自身對齊值和指定對齊值中較小的那個。
對於結構體
1、不但結構體的成員有有效對齊值,結構體本身也有對齊值,這主要是考慮結構體的數組,對於結構體或者類,要將其補齊爲其有效對齊值的整數倍。結構體的有效對齊值是其最大數據成員的自身對齊值;
2、存放成員的起始地址必須是該成員有效對齊值的整數倍。
具體看下下面的例子讀者應該就會明白。

#include<iostream>
using namespace std;
#pragma pack(push,1)//可爲1,2,4,8
struct s1
{
	char c1;
	char c2;
	int x;
};
struct s2
{
	char c1;
	int x;
	char c2;
};
struct s3
{
	char c1;
	int x;
	short sx;
	char c3;
};

int main()
{
	char test;
	cout << "s1:"<<sizeof(s1)<<endl;
	cout << "s2:" << sizeof(s2) << endl;
	cout << "s3:" << sizeof(s3) << endl;
	system("pause");
	return 0;
}
#pragma pack(pop)

當對齊字節爲1時:
在這裏插入圖片描述
當對齊字節爲4(默認)時:
在這裏插入圖片描述

參考引用:
字節對齊參考
結構體對齊方式參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章