1. 程式人生 > >linux中結構體對齊【轉】

linux中結構體對齊【轉】

src double 無需 fine types 查看 真理 blog 多個

轉自:https://blog.csdn.net/suifengpiao_2011/article/details/47260085

linux中定義對齊字節

typedef struct sdk_handler {
char comm_ver[10];
char name[20];
char reserve[20];
}PACKED sdk_handler_t;

#define PACKED //__attribute__((aligned(1),packed)) // 一字節對齊

首先我們先看看下面的C語言的結構體:

[cpp] view plaincopy技術分享圖片
技術分享圖片
  1. typedef struct MemAlign
  2. {
  3. int a;
  4. char b[3];
  5. int c;
  6. }MemAlign;

以上這個結構體占用內存多少空間呢?也許你會說,這個簡單,計算每個類型的大小,將它們相加就行了,以32為平臺為例,int類型占4字節,char占用1字節,所以:4 + 3 + 4 = 11,那麽這個結構體一共占用11字節空間。好吧,那麽我們就用實踐來證明是否正確,我們用sizeof運算符來求出這個結構體占用內存空間大小,sizeof(MemAlign),出乎意料的是,結果居然為12?看來我們錯了?當然不是,而是這個結構體被優化了,這個優化有個另外一個名字叫“對齊”,那麽這個對齊到底做了什麽樣的優化呢,聽我慢慢解釋,再解釋之前我們先看一個圖,圖如下:

技術分享圖片

相信學過匯編的朋友都很熟悉這張圖,這張圖就是CPU與內存如何進行數據交換的模型,其中,左邊藍色的方框是CPU,右邊綠色的方框是內存,內存上面的0~3是內存地址。這裏我們這張圖是以32位CPU作為代表,我們都知道,32位CPU是以雙字(DWORD)為單位進行數據傳輸的,也正因為這點,造成了另外一個問題,那麽這個問題是什麽呢?這個問題就是,既然32位CPU以雙字進行數據傳輸,那麽,如果我們的數據只有8位或16位數據的時候,是不是CPU就按照我們數據的位數來進行數據傳輸呢?其答案是否定的,如果這樣會使得CPU硬件變的更復雜,所以32位CPU傳輸數據無論是8位或16位都是以雙字進行數據傳輸。那麽也罷,8位或16位一樣可以傳輸,但是,事情並非像我們想象的那麽簡單,比如,一個int類型4字節的數據如果放在上圖內存地址1開始的位置,那麽這個數據占用的內存地址為1~4,那麽這個數據就被分為了2個部分,一個部分在地址0~3中,另外一部分在地址4~7中,又由於32位CPU以雙字進行傳輸,所以,CPU會分2次進行讀取,一次先讀取地址0~3中內容,再一次讀取地址4~7中數據,最後CPU提取並組合出正確的int類型數據,舍棄掉無關數據。那麽反過來,如果我們把這個int類型4字節的數據放在上圖從地址0開始的位置會怎樣呢?讀到這裏,也許你明白了,CPU只要進行一次讀取就可以得到這個int類型數據了。沒錯,就是這樣,這次CPU只用了一個周期就得到了數據,由此可見,對內存數據的擺放是多麽重要啊,擺放正確位置可以減少CPU的使用資源。

那麽,內存對齊有哪些原則呢?我總結了一下大致分為三條:
第一條:第一個成員的首地址為0
第二條:每個成員的首地址是自身大小的整數倍
第二條補充:以4字節對齊為例,如果自身大小大於4字節,都以4字節整數倍為基準對齊。

若內嵌結構體,則內嵌結構體的首地址也要符合此條,只不過自身大小用內嵌結構體的最大成員來表示。補齊時,內嵌結構體和外部結構體都要滿足補齊,內嵌機構體中的補齊以其內部最大成員表示;外部結構體以自身基礎成員和內嵌結構體的最大成員之間的較大者表示。


第三條:最後以結構總體對齊。
第三條補充:以4字節對齊為例,取結構體中最大成員類型倍數,如果超過4字節,都以4字節整數倍為基準對齊。(其中這一條還有個名字叫:“補齊”,補齊的目的就是多個結構變量挨著擺放的時候也滿足對齊的要求。)

上述的三原則聽起來還是比較抽象,那麽接下來我們通過一個例子來加深對內存對齊概念的理解,下面是一個結構體,我們動手算出下面結構體一共占用多少內存?假設我們以32位平臺並且以4字節對齊方式:

[cpp] view plaincopy技術分享圖片技術分享圖片
  1. #pragma pack(4)
  2. typedef struct MemAlign
  3. {
  4. char a[18];
  5. double b;
  6. char c;
  7. int d;
  8. short e;
  9. }MemAlign;

下圖為對齊後結構如下:

技術分享圖片

我們就以這個圖來講解是如何對齊的:
第一個成員(char a[18]):首先,假設我們把它放到內存開始地址為0的位置,由於第一個成員占18個字節,所以第一個成員占用內存地址範圍為0~18。
第二個成員(double b):由於double類型占8字節,又因為8字節大於4字節,所以就以4字節對齊為基準。由於第一個成員結束地址為18,那麽地址18並不是4的整數倍,我們需要再加2個字節,也就是從地址20開始擺放第二個成員。
第三個成員(char c):由於char類型占1字節,任意地址是1字節的整數倍,所以我們就直接將其擺放到緊接第二個成員之後即可。
第四個成員(int d):由於int類型占4字節,但是地址29並不是4的整數倍,所以我們需要再加3個字節,也就是從地址32開始擺放這個成員。
第五個成員(short e):由於short類型占2字節,地址36正好是2的整數倍,這樣我們就可以直接擺放,無需填充字節,緊跟其後即可。
這樣我們內存對齊就完成了。但是離成功還差那麽一步,那是什麽呢?對,是對整個結構體補齊,接下來我們就補齊整個結構體。那麽,先讓我們回顧一下補齊的原則:“以4字節對齊為例,取結構體中最大成員類型倍數,如果超過4字節,都以4字節整數倍為基準對齊。”在這個結構體中最大類型為double類型(占8字節),又由於8字節大於4字 節,所以我們還是以4字節補齊為基準,整個結構體結束地址為38,而地址38並不是4的整數倍,所以我們還需要加額外2個字節來填充結構體,如下圖紅色的就是補齊出來的空間:

技術分享圖片

到此為止,我們內存對齊與補齊就完畢了!接下來我們用實驗來證明真理,程序如下:

[cpp] view plaincopy技術分享圖片技術分享圖片
  1. #include <stdio.h>
  2. #include <memory.h>
  3. // 由於VS2010默認是8字節對齊,我們
  4. // 通過預編譯來通知編譯器我們以4字節對齊
  5. #pragma pack(4)
  6. // 用於測試的結構體
  7. typedef struct MemAlign
  8. {
  9. char a[18]; // 18 bytes
  10. double b; // 08 bytes
  11. char c; // 01 bytes
  12. int d; // 04 bytes
  13. short e; // 02 bytes
  14. }MemAlign;
  15. int main()
  16. {
  17. // 定義一個結構體變量
  18. MemAlign m;
  19. // 定義個以指向結構體指針
  20. MemAlign *p = &m;
  21. // 依次對各個成員進行填充,這樣我們可以
  22. // 動態觀察內存變化情況
  23. memset( &m.a, 0x11, sizeof(m.a) );
  24. memset( &m.b, 0x22, sizeof(m.b) );
  25. memset( &m.c, 0x33, sizeof(m.c) );
  26. memset( &m.d, 0x44, sizeof(m.d) );
  27. memset( &m.e, 0x55, sizeof(m.e) );
  28. // 由於有補齊原因,所以我們需要對整個
  29. // 結構體進行填充,補齊對齊剩下的字節
  30. // 以便我們可以觀察到變化
  31. memset( &m, 0x66, sizeof(m) );
  32. // 輸出結構體大小
  33. printf( "sizeof(MemAlign) = %d", sizeof(m) );
  34. }

程序運行過程中,查看內存如下:
技術分享圖片
其中,各種顏色帶下劃線的代表各個成員變量,藍色方框的代表為內存對齊時候填補的多余字節,由於這裏看不到補齊效果,我們接下來看下圖,下圖籃框包圍的字節就是與上圖的交集以外的部分就是補齊所填充的字節。

技術分享圖片

在最後,我在談一談關於補齊的作用,補齊其實就是為了讓這個結構體定義的數組變量時候,數組內部,也同樣滿足內存對齊的要求,為了更好的理解這點,我做了一個跟本例子相對照的圖:

技術分享圖片

補充:

1)對於結構體中有數組的,將數組作為基本數據類型來處理對齊;

2)對於結構體內嵌套結構體成員的,結構體成員的對齊和補齊原則:已最大成員作為內結構體整體的大小去匹配;

--------------------- 本文來自 漫步者2011 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/suifengpiao_2011/article/details/47260085?utm_source=copy

linux中結構體對齊【轉】