C語言結構體字節對齊的實現深入分析

前言

本教程可能會用到一點匯編的知識,看不懂沒關系,知道是那個意思就行瞭。使用的工具是vs2010。

一、什麼是字節對齊

字節對齊是字節按照一定規則在空間上排列。

現代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經常在特 定的內存地址訪問,這就需要各種類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

在我們之前寫程序的時候可能會發現有的時候你定義的變量是一個字節,但是他在內存中依然按照四個字節給你存儲,因為在32為系統中,4字節對齊執行效率是最快的。這就是一種犧牲內存換取性能的方案。

舉個栗子

我們知道,全局變量在在內存中是有一個固定的地址的,如果不重新編譯的話,這個地址是不會改變的。所以我們拿全局變量舉例。

如下,我們定義兩個全局變量:

一個是char(單字節)的一個是int(4字節的)

然後我們在main函數中給他們賦值,如下:

然後斷點、F7編譯、F5調試、ALT+8轉到反匯編:

可以看到他們之間差值為4字節

再次測試,如下:

反匯編:

差值為2。

因為字節對齊的原因,int類型的起始地址必須是4的整數倍,short類型的起始地址也必須是2的倍數,當然char就沒有那麼約束瞭,因為它隻是一個字節的。並不是說char類型的變量與int變量的差值是因為int類型占瞭四個字節所以差值才為4 。

再做測試,如下:

打亂順序。查看反匯編:

總結如圖。

再次強掉、因為字節對齊的原因,變量的起始地址必須是變量字節大小的整數倍

二、結構體字節對齊

結構體中成員的存儲方式也是按照上面的字節對齊方式進行存儲的。不過有一點區別:結構體的起始地址是其最寬數據類型的整數倍(千萬不要死記:結構體寬度就是最寬數據類型的整數倍,因為這樣容易忘,下面我來和大傢分析為什麼是這樣,知道為什麼才能記得更久)。

舉個栗子

結果:

我們分析一下:

int 4字節、char 1字節、double 8字節,按理說應該是13字節對吧。

分析如下:

int類型占瞭4個字節,這個時候char類型因為字節對齊的原因、他的起始地址是1的整數倍,所以它可以挨著int類型。但是double,他是8個字節的,由於字節對齊的原因、他的起始地址必須是8的倍數,所以他和char類型之間會差3。但是int、char、double他們是一個結構體的成員,不是分開存儲的。所以char和double之間空的三個字節因為字節對齊的原因必須補齊。

再來:

可以先猜測一下結果。

結果如下:

這裡大傢可以試著推測,隻需要記住因為字節對齊的原因,char起始地址必須是1的整數倍、int必須是4的整數倍等等。

三、#pragma pack()的使用

當對內存要求較高的時候,我們不得不拋棄性能。這個時候可以通過#pragma pack(n)來強制結構體成員的對齊方式

#pragma pack(1)

struct st_info

{

char a;

int b;

};

#pragma pack()

<1> #pragma pack(n)中的n用來設定變量以n字節的方式對齊,可以設定的值包括:1、2、4、8,VC編譯器默認是8。

<2> 若需要強制取消字節對齊方式,則可使用#pragma pak()取消。

舉個栗子:

示例代碼:

#include <stdio.h>
#include <Windows.h>
#pragma pack(2)
struct stinfo
{
	char   t;
	int    x;
	char   y;
	double m;
};
#pragma pack()
int main()
{
	printf("%d\n", sizeof(stinfo));
 
	while(1);
	return 0;
}

如下:

這裡我們讓結構體以2字節的方式對齊,所以猜想:

char t因為字節對齊的原因起始地址應該是2的整數倍,但是他是首地址,不用管。

t = 1個字節。

int x類型因為pack(2)的原因,起始地址應該是2的整數倍,所以它和chart的差值應該為2,也就是補1個字節。

t+1+x = 6個字節

到這裡,t和x已經占有6個字節。

char y因為pack(2)的原因,他的起始地址應該是2的整數倍,但是上面六個字節正好是2的倍數。

t+1+x+y = 7個字節

到這裡t、x、y三個成員已經占有7個字節。

double m因為pack(2)的原因,起始地址不得不是2的整數倍,但是上面7個字節顯然不對,所以因為字節對齊的原因,上面t、x、y三個成員需要再補一個字節,這個時候加上double的8個字節。

t+1+x+y+1+8 = 16個字節

結果如下:

猜想正確。

如果我們不強制字節對齊的話:

char t一個字節

int x起始地址必須是4的整數倍,所以t必須補3個字節

x+t+3 = 8個字節

char y一個字節

x+t+3+y = 9個字節

double m起始地址必須是8的倍數,所以x+t+y需要補7個字節。

x+t+3+y+7+8 = 24個字節

結果如下:

對於pack(1)、pack(4)、pack(8)大傢自己嘗試下吧。

總結

通過上面的講解,想必大傢應該知道結構體的字節數為什麼是成員中最寬數據類型字節數的整數倍瞭的,但是還是那句話,不要死記這一點,明白為什麼才是重點、才能記得更久。

因為字節對齊的原因、結構體中成員的起始地址、假設該成員是int類型,那麼它的起始地址必須是4的整數倍,如果是double類型,那麼他的起始地址必須是8的整數倍。當然也可以強制他們的起始地址是一個固定數字(1、2、4、8)的整數倍。通過#pragma pack(n)即可。

到此這篇關於C語言結構體字節對齊的實現深入分析的文章就介紹到這瞭,更多相關C語言結構體字節對齊內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: