1. 程式人生 > >結構體位元組對齊

結構體位元組對齊

1、什麼是位元組對齊

現代計算機中,記憶體空間按照位元組劃分,理論上可以從任何起始地址訪問任意型別的變數。但實際中在訪問特定型別變數時經常在特定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序一個接一個地存放,這就是對齊。

2、位元組對齊的原因和作用

不同硬體平臺對儲存空間的處理上存在很大的不同。某些平臺對特定型別的資料只能從特定地址開始存取,而不允許其在記憶體中任意存放。例如Motorola 68000 處理器不允許16位的字存放在奇地址,否則會觸發異常,因此在這種架構下程式設計必須保證位元組對齊。

但最常見的情況是,如果不按照平臺要求對資料存放進行對齊,會帶來存取效率上的損失。比如32位的Intel處理器通過匯流排訪問(包括讀和寫)記憶體資料。每個匯流排週期從偶地址開始訪問32位記憶體資料,記憶體資料以位元組為單位存放。如果一個32位的資料沒有存放在4位元組整除的記憶體地址處,那麼處理器就需要2個匯流排週期對其進行訪問,顯然訪問效率下降很多。

因此,通過合理的記憶體對齊可以提高訪問效率。為使CPU能夠對資料進行快速訪問,資料的起始地址應具有“對齊”特性。比如4位元組資料的起始地址應位於4位元組邊界上,即起始地址能夠被4整除。

此外,合理利用位元組對齊還可以有效地節省儲存空間。但要注意,在32位機中使用1位元組或2位元組對齊,反而會降低變數訪問速度。因此需要考慮處理器型別。還應考慮編譯器的型別。在VC/C++和GNU GCC中都是預設是4位元組對齊。

3、位元組對齊原則

 * 1、結構體變數的首地址能被對齊數整除
 * 2、每個成員相對首地址的offset都是對齊數的整數倍
 * 3、結構體總的大小為對齊數的整數倍
 * 4、不同的編譯器對齊數不一樣,gcc的對齊數是4,不是max_width

4、設定對齊方式

主要是更改C編譯器的預設位元組對齊方式。

在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:

使用偽指令#pragma pack(n):C編譯器將按照n個位元組對齊;
使用偽指令#pragma pack(): 取消自定義位元組對齊方式。

另外,還有如下的一種方式(GCC特有語法):

__attribute__((aligned (n))): 讓所作用的結構成員對齊在n位元組自然邊界上。如果結構體中有成員的長度大於n,則按照最大成員的長度來對齊。
__attribute__ ((packed)): 取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。

4、程式碼示例

/**
 * 32位OS預設按4位元組對齊
 */
struct A{
	int a;
	char b;
	short c;
}AT;

struct B{
	char b;
	int a;
	short c;
}BT;

#pragma pack(2) /* 按2位元組對齊 */
struct C{
	char b;
	int a;
	short c;
}CT;
#pragma pack()

#pragma pack(1) /* 按1位元組對齊 ,則大小為型別長度總和*/
struct D{
	char b;
	int a;
	short c;
}DT;
#pragma pack()

#define GNUC_PACKED __attribute__((aligned(2)))  /* 按2位元組對齊 */
struct KT{
     char  b;
     int   a;
     short c;
}GNUC_PACKED;

struct Tone{
	int m1;
	char m2;
	float m3;
	union Tun{
		char u1[5];
		int u2[2];
	}UT;
	double m4;
}ToneT;

void testStSize()
{
	printf("%d,%d\n",sizeof(AT),sizeof(BT));//8,12
	printf("%d,%d\n",sizeof(CT),sizeof(DT));//8,7
	printf("%d\n",sizeof(ToneT));//32
}