C結構體中數據的內存對齊問題

轉自:http://blog.csdn.net/hbyh/archive/2008/01/23/2061471.aspx

 

1.
先看下面的例子:
struct A{
   char c1;
   int i;
   short s;
   int j;
}a;

struct B{
   int i;
   int j;  
   short s;
   char c1;
}b;

結構A沒有遵守字節對齊原則(爲了區分,我將它叫做對齊聲明原則),結構B遵守了。我們來看看在x86上會出現什麼結果。先打印出a和b的各個成員的地址。會看到a中,各個成員間的間距是4個字節。b中,i和j,j和s都間距4個字節,但是s和c1間距2個字節。所以:
sizeof(a) = 16
sizeof(b) = 12
爲 什麼會有這樣的結果呢?這就是x86上字節對齊的作用。爲了加快程序執行的速度,一些體系結構以對齊的方式設計,通常以字長作爲對齊邊界。對於一些結構體 變量,整個結構要對齊在內部成員變量最大的對齊邊界,如B,整個結構以4爲對齊邊界,所以sizeof(b)爲12,而不是11。
對於A來講,雖 然聲明的時候沒有對齊,但是根據打印出的地址來看,編譯器已經自動爲其對齊了,所以每個成員的間距是4。在x86下,聲明A與B唯一的差別,僅在於A多浪 費了4個字節內存。(是不是某些特定情況下,B比A執行更快,這個還需要討論。比如緊挨的兩條分別取s和c1的指令)
如果體系結構是不對齊的,A中的成員將會一個挨一個存儲,從而sizeof(a)爲11。顯然對齊更浪費了空間。那麼爲什麼要使用對齊呢?
體 繫結構的對齊和不對齊,是在時間和空間上的一個權衡。對齊節省了時間。假設一個體繫結構的字長爲w,那麼它同時就假設了在這種體系結構上對寬度爲w的數據 的處理最頻繁也是最重要的。它的設計也是從優先提高對w位數據操作的效率來考慮的。比如說讀寫時,大多數情況下需要讀寫w位數據,那麼數據通道就會是w 位。如果所有的數據訪問都以w位對齊,那麼訪問還可以進一步加快,因爲需要傳輸的地址位減少,尋址可以加快。大多數體系結構都是按照字長來對齊訪問數據 的。不對齊的時候,有的會出錯,比如MIPS上會產生bus error,而x86則會進行多次訪問來拼接得到的結果,從而降低執行效率。

有些體系結構是必須要求對齊的,如sparc,MIPS。它們在硬件的設計上就強制性的要求對齊。不是因爲它們作不到對齊的訪問,而是它們認爲這樣沒有意義。它們追求的是速度。

上面講了體系結構的對齊。在IA-32上面,sizeof(a)爲16,就是對齊的結果。下面我們來看,爲什麼變量聲明的時候也要儘量對齊。
我 們看到,結構A的聲明並不對齊,但是它的成員地址仍是以4爲邊界對齊的(成員間距爲4)。這是編譯器的功勞。因爲我所用的編譯器gcc,默認是對齊的。而 x86可以處理不對齊的數據訪問,所以這樣聲明程序並不會出錯。但是對於其他結構,只能訪問對齊的數據,而編譯器又不小心設置了不對齊的選項,則代碼就不 能執行了。如果按照B的方式聲明,則不管編譯器是否設置了對齊選項,都能夠正確的訪問數據。

目前的開發普遍比較重視性能,所以對齊的問題,有三種不同的處理方法:
1)     採用B的方式聲明
2)     對於邏輯上相關的成員變量希望放在靠近的位置,就寫成A的方式。有一種做法是顯式的插入reserved成員:
          struct A{
            char c1;
            char reserved1[3];
            int i;
            short s;
            char reserved2[2];
            int j;
}a;
3)     隨便怎麼寫,一切交給編譯器自動對齊。

代碼中關於對齊的隱患,很多是隱式的。比如在強制類型轉換的時候。下面舉個例子:
unsigned int ui_1=0x12345678;
unsigned char *p=NULL;
unsigned short *us_1=NULL;

p=&ui_1;
*p=0x00;
us_1=(unsigned short *)(p+1);
*us_1=0x0000;
最後兩句代碼,從奇數邊界去訪問unsigned short型變量,顯然不符合對齊的規定。在x86上,類似的操作只會影響效率,但是在MIPS或者sparc上,可能就是一個bus error(我沒有試)。
有 些人喜歡通過移動指針來操作結構中的成員(比如在linux操作struct sk_buff的成員),但是我們看到,A中(&c1+1) 決不等於&i。不過B中(&s+2)就是 &c1了。所以,我們清楚了結構中成員的存放位置,才能編寫無錯的代碼。同時切記,不管對於結構,數組,或者普通的變量,在作強制類型轉換時一定 要多看看:)不過爲了不那麼累,還是遵守聲明對齊原則吧!(這個原則是說變量儘量聲明在它的對齊邊界上,而且在節省空間的基礎上)

2.C/C++函數調用方式
我們當然早就知道,C/C++中的函數調用,都是以值傳遞的方式,而不是參數傳遞。那麼,值傳遞是如何實現的呢?
函數調用前的典型彙編碼如下:
push    %eax
call    0x401394 <test__Fc>
add     $0x10,%esp
首先,入棧的是實參的地址。由於被調函數都是對地址進行操作,所以就能夠理解值傳遞的原理和參數是引用時的情況了。
Call ***, 是要調用函數了,後面的地址,就是函數的入口地址。Call指令等價於:
    PUSH IP
    JMP ***
首先把當前的執行地址IP壓棧,然後跳轉到函數執行。
執行完後,被調函數要返回,就要執行RET指令。RET等價於POP IP,恢復CALL之前的執行地址。所以一旦使用CALL指令,堆棧指針SP就會自動減2,因爲IP的值進棧了。

函數的參數進棧的順序是從右到左,這是C與其它語言如pascal的不同之處。函數調用都以以下語句開始:
push    %ebp
mov     %esp,%ebp
首先保存BP的值,然後將當前的堆棧指針傳遞給BP。那麼現在BP+2就是IP的值(16位register的情況),BP+4放第一個參數的值,BP+6放第二個參數……。函數在結束前,要執行POP BP。
    
C/C++語言默認的函數調用方式,都是由主調用函數進行參數壓棧並且恢復堆棧,實參的壓棧順序是從右到左,最後由主調函數進行堆棧恢復。由於主調用函數管理堆棧,所以可以實現變參函數。
對於WINAPI和CALLBACK函數,在主調用函數中負責壓棧,在被調用函數中負責彈出堆棧中的參數,並且負責恢復堆棧。因此不能實現變參函數。

----------------------------------------------------------------------------------------------------------------------------------------------------

結構體的大小是一個令人迷惑不解的問題,不信,我出一題讓你算算看:
enum DataType{IntData,CharData,VcharData};
struct Item    
{
      char ItemNAme[30];
        DataType ItemType;
        char ItemDecr[50];
        int ItemLength;
};
在你使用sizeof之前你能知道它的大小嗎?我想即使你用sizeof得出結果後,結果還是會讓你大吃一驚的:怎麼是這個?
一.爲什麼要對齊?
《Windows核心編程》裏這樣說:當CPU訪問正確對齊的數據時,它的運行效率最高,當數據大小的數據模數 的內存地址是0時,數據是對齊的。例如:WORD值應該是總是從被2除盡的地址開始,而DWORD值應該總是從被4除盡的地址開始,數據對齊不是內存結構 的一部分,而是CPU結構的一部分。當CPU試圖讀取的數值沒有正確的對齊時,CPU可以執行兩種操作之一:產生一個異常條件;執行多次對齊的內存訪問, 以便讀取完整的未對齊數據,若多次執行內存訪問,應用程序的運行速度就會慢。在最好的情況下,是兩倍的時間,有時更長。
二.成員變量對齊的原理
我花了一個上午,看了一些資料,總算把這個問題搞明白了。下面我以一些例子說明結構體成員變量的對齊問題。
對於
struct s1
{
char a;
long int d;
double c;
};
這個結構體的大小是16。編譯器默認的一般是8字節對齊。a的大小是1,它就按1字節對齊(因爲比指定的8 小),存諸在0偏移的地方;b大小爲4,它就按4字節對齊(因爲比指定的8小),存在偏移4——7的位置,c大小爲8,存在8——15的位置。這樣3個成 員共佔用了16個字節。由於該結構最大成員c大小爲8,所以結構按8字節對齊,16按8園整還是16,因此sizeof s1 = 16.
而對於
struct s2
{
char a;
long int d;
double c;
char e;
};
這個結構體的大小是24。前3個成員和上面一樣存諸,d在4——7位置,c在8——15位置,但e按1字節對齊,存在偏移位置16,這樣4個成員佔用了17個字節。由於該結構的最大的數據成員c的大小是8個字節,所以17對8園整得24。
當然你可以使用#pragma指令指定編譯器按4字節對齊。即
#pragma pack(4)       // 這裏也可以是#pragma pack(push,4)
struct s1
{
char a;
long int d;
double c;
};
struct s2
{
char a;
long int d;
double c;
char e;
};
這時s1的大小還是16,而s2的大小變爲20。我們來分析一下,對s1來說,按4字節對齊和按8字節對齊個數 據成員的存諸位置是一樣的,只不過是最後一部園整時,16對4園整還是16。對s2就不一樣了,a的大小爲1(比指定的4字節對齊要小),按1字節對齊, 存諸在0位置,d的大小是4(大於等於指定的4字節),按4字節對齊,存諸在4——7位置,c的大小是8(大於指定的4字節),按4字節對齊,這樣存諸在 8——15,e的大小是1,存儲在位置16,這樣整個結構體的長度是17,17對4園整,得20。你也看到並不是指定編譯器按4字節對齊就按4字節對齊 的。比如下面的結構體:
#pragma pack(4)
struct TestStruct2
{
    char m1[11];
    short m2;
};
你知道它的大小嗎?是14。因爲m1按1字節對齊,存諸在0——11位置,m2按2字節對齊,存諸在12—— 13位置。結構體佔用13個字節,因爲結構體內最大的成員的數據類型是short,大小爲2,比指定的對齊字節4小,所以13對2園整,得14。綜的說來 就是結構體成員的對齊是用成員本身的大小和#pragma pack(push,n)中的n中較小的數對齊,例如如果成員大小爲2,而你指定的對齊方式是4,則該成員按2對齊;結構本身的對其是用結構中最大成員的 大小和#pragma pack(push,n)中的n較小的數對齊,即最後的園整,例如如果結構中最大成員大小8,而你指定對齊是16,則結構本身按8對齊。
開頭題目的大小是92。你算到了嗎?下面這個結構體的大小又是多少呢?
enum DataType{IntData,CharData,VcharData};
#pragma pack(2)
struct Item    
{
        char ItemNAme[30];
    DataType ItemType;
    char ItemDecr[50];
    int ItemLength;
};
----------------------------------------------------------------------------------------------------------------------------------------------------
當在C中定義了一個結構類型時,它的大小是否等於各字段(field) 大小之和?編譯器將如何在內存中放置這些字段?ANSI C對結構體的內存佈局有什麼要求?而我們的程序又能否依賴這種佈局?這些問題或許對不少朋友來說還有點模糊,那麼本文就試着探究它們背後的祕密。 首先,至少有一點可以肯定,那就是ANSI C保證結構體中各字段在內存中出現的位置是隨它們的聲明順序依次遞增的,並且第一個字段的首地址等於整個結構體實例的首地址。比如有這樣一個結構體:

struct vector{int x,y,z;} s;
int *p,*q,*r;
struct vector *ps;

p = &s.x;
q = &s.y;
r = &s.z;
ps = &s;

assert(p < q);
assert(p < r);
assert(q < r);
assert((int*)ps == p);
// 上述斷言一定不會失敗

這時,有朋友可能會問:"標準是否規定相鄰字段在內存中也相鄰?"。 唔,對不起,ANSI C沒有做出保證,你的程序在任何時候都不應該依賴這個假設。那這是否意味着我們永遠無法勾勒出一幅更清晰更精確的結構體內存佈局圖?哦,當然不是。不過先讓我們從這個問題中暫時抽身,關注一下另一個重要問題————內存對齊。
許多實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它爲4或8)的倍數,這就是所謂的內存對齊,而這個k則被稱爲該數據類型的對齊模數(alignment modulus)。當一種類型S的對齊模數與另一種類型T的對齊模數的比值是大於1的整數,我們就稱類型S的對齊要求比T強(嚴格),而稱T比S弱(寬鬆)。這種強制的要求一來簡化了處理器與內存之間傳輸系統的設計,二來可以提升讀取數據的速度。比如這麼一種處理器,它每次讀寫內存的時候都從某個8倍數的地址開始,一次讀出或寫入8個字節的數據,假如軟件能保證double類型的數據都從8倍數地址開始,那麼讀或寫一個double類型數據就只需要一次內存操作。否則,我們就可能需要兩次內存操作才能完成這個動作,因爲數據或許恰好橫跨在兩個符合對齊要求的8字節內存塊上。某些處理器在數據不滿足對齊要求的情況下可能會出錯,但是Intel的IA32架構的處理器則不管數據是否對齊都能正確工作。不過Intel奉勸大家,如果想提升性能,那麼所有的程序數據都應該儘可能地對齊。Win32平臺下的微軟C編譯器(cl.exe for 80x86)在默認情況下采用如下的對齊規則: 任何基本數據類型T的對齊模數就是T的大小,即sizeof(T)。比如對於double類型(8字節),就要求該類型數據的地址總是8的倍數,而char類型數據(1字節)則可以從任何一個地址開始。Linux下的GCC奉行的是另外一套規則(在資料中查得,並未驗證,如錯誤請指正):任何2字節大小(包括單字節嗎?)的數據類型(比如short)的對齊模數是2,而其它所有超過2字節的數據類型(比如long,double)都以4爲對齊模數。
現在回到我們關心的struct上來。ANSI C規定一種結構類型的大小是它所有字段的大小以及字段之間或字段尾部的填充區大小之和。嗯?填充區?對,這就是爲了使結構體字段滿足內存對齊要求而額外分配給結構體的空間。那麼結構體本身有什麼對齊要求嗎?有的,ANSI C標準規定結構體類型的對齊要求不能比它所有字段中要求最嚴格的那個寬鬆,可以更嚴格(但此非強制要求,VC7.1就僅僅是讓它們一樣嚴格)。我們來看一個例子(以下所有試驗的環境是Intel Celeron 2.4G + WIN2000 PRO + vc7.1,內存對齊編譯選項是"默認",即不指定/Zp與/pack選項):
typedef struct ms1
{
char a;
int b;
} MS1;

假設MS1按如下方式內存佈局(本文所有示意圖中的內存地址從左至右遞增):

+---------------------------+
| | |
| a | b |
| | |
+---------------------------+
1 Byte 4 byte


因爲MS1中有最強對齊要求的是b字段(int),所以根據編譯器的對齊規則以及ANSI C標準,MS1對象的首地址一定是4(int類型的對齊模數)的倍數。那麼上述內存佈局中的b字段能滿足int類型的對齊要求嗎?嗯,當然不能。如果你是編譯器,你會如何巧妙安排來滿足CPU的癖好呢?呵呵,經過1毫秒的艱苦思考,你一定得出瞭如下的方案:
_______________________________________
| |///////////| |
| a |//padding//| b |
| |///////////| |
+-------------------------------------+
Bytes: 1 3 4

這個方案在a與b之間多分配了3個填充(padding)字節,這樣當整個struct對象首地址滿足4字節的對齊要求時,b字段也一定能滿足int型的4字節對齊規定。那麼sizeof(MS1)顯然就應該是8,而b字段相對於結構體首地址的偏移就是4。非常好理解,對嗎?現在我們把MS1中的字段交換一下順序:
typedef struct ms2
{
int a;
char b;
} MS2;

或許你認爲MS2比MS1的情況要簡單,它的佈局應該就是
_______________________
| | |
| a | b |
| | |
+---------------------+
Bytes: 4 1

因爲MS2對象同樣要滿足4字節對齊規定,而此時a的地址與結構體的首地址相等,所以它一定也是4字節對齊。嗯,分析得有道理,可是卻不全面。讓我們來考慮一下定義一個MS2類型的數組會出現什麼問題。C標準保證,任何類型(包括自定義結構類型)的數組所佔空間的大小一定等於一個單獨的該類型數據的大小乘以數組元素的個數。換句話說,數組各元素之間不會有空隙。按照上面的方案,一個MS2數組array的佈局就是:
|<- array[1] ->|<- array[2] ->|<- array[3] .....
__________________________________________________________
| | | | |
| a | b | a | b |.............
| | | | |
+----------------------------------------------------------
Bytes: 4 1 4 1

當數組首地址是4字節對齊時,array[1].a也是4字節對齊,可是array[2].a呢?array[3].a ....呢?可見這種方案在定義結構體數組時無法讓數組中所有元素的字段都滿足對齊規定,必須修改成如下形式:
___________________________________
| | |///////////|
| a | b |//padding//|
| | |///////////|
+---------------------------------+
Bytes: 4 1 3

現在無論是定義一個單獨的MS2變量還是MS2數組,均能保證所有元素的所有字段都滿足對齊規定。那麼sizeof(MS2)仍然是8,而a的偏移爲0,b的偏移是4。
好的,現在你已經掌握了結構體內存佈局的基本準則,嘗試分析一個稍微複雜點的類型吧。
typedef struct ms3
{
char a;
short b;
double c;
} MS3;

我想你一定能得出如下正確的佈局圖:

padding
|
_____v_________________________________
| |/| |/////////| |
| a |/| b |/padding/| c |
| |/| |/////////| |
+-------------------------------------+
Bytes: 1 1 2 4 8

sizeof(short)等於2,b字段應從偶數地址開始,所以a的後面填充一個字節,而sizeof(double)等於8,c
字段要從8倍數地址開始,前面的a、b字段加上填充字節已經有4 bytes,所以b後面再填充4個字節就可以保證c字段的對齊要求了。sizeof(MS3)等於16,b的偏移是2,c的偏移是8。接着看看結構體中字段還是結構類型的情況:
typedef struct ms4
{
char a;
MS3 b;
} MS4;

MS3中內存要求最嚴格的字段是c,那麼MS3類型數據的對齊模數就與double的一致(爲8),a字段後面應填充
7個字節,因此MS4的佈局應該是:
_______________________________________
| |///////////| |
| a |//padding//| b |
| |///////////| |
+-------------------------------------+
Bytes: 1 7 16

顯然,sizeof(MS4)等於24,b的偏移等於8。
在實際開發中,我們可以通過指定/Zp編譯選項來更改編譯器的對齊規則。比如指定/Zpn(VC7.1中n可以是1、2、4、8、16)就是告訴編譯器最大對齊模數是n。在這種情況下,所有小於等於n字節的基本數據類型的對齊規則與默認的一樣,但是大於n個字節的數據類型的對齊模數被限制爲n。事實上,VC7.1的默認對齊選項就相當於/Zp8。仔細看看MSDN對這個選項的描述,會發現它鄭重告誡了程序員不要在MIPS和Alpha平臺上用/Zp1和/Zp2選項,也不要在16位平臺上指定/Zp4和/Zp8(想想爲什麼?)。改變編譯器的對齊選項,對照程序運行結果重新分析上面4種結構體的內存佈局將是一個很好的複習。
到了這裏,我們可以回答本文提出的最後一個問題了。結構體的內存佈局依賴於CPU、操作系統、編譯器及編譯時的對齊選項,而你的程序可能需要運行在多種平臺上,你的源代碼可能要被不同的人用不同的編譯器編譯(試想你爲別人提供一個開放源碼的庫),那麼除非絕對必需,否則你的程序永遠也不要依賴這些詭異的內存佈局。順便說一下,如果一個程序中的兩個模塊是用不同的對齊選項分別編譯的,那麼它很可能會產生一些非常微妙的錯誤。如果你的程序確實有很難理解的行爲,不防仔細檢查一下各個模塊的編譯選項。
思考題:請分析下面幾種結構體在你的平臺上的內存佈局,並試着尋找一種合理安排字段聲明順序的方法以盡量節省內存空間。
A. struct P1 { int a; char b; int c; char d; };
B. struct P2 { int a; char b; char c; int d; };
C.
struct P3 { short a[3]; char b[3]; };
D.
struct P4 { short a[3]; char *b[3]; };
E. struct P5 { struct P2 *a; char b; struct P1 a[2]; };

參考資料:
【1】《深入理解計算機系統(修訂版)》,
(著)Randal E.Bryant; David O'Hallaron,
(譯)龔奕利 雷迎春,
中國電力出版社,2004

【2】《C: A Reference Manual》(影印版),
(著)Samuel P.Harbison; Guy L.Steele,
人民郵電出版社,2003

-----------------------------------------------------


默認對齊方式:
struct name1
    {
    char   str;     佔用空間:2個字節
    short x;       佔用空間:2個字節
    int    num;     佔用空間:4個字節
    double xx;     佔用空間:8個字節
    };
struct name2
{
    char str;    佔用空間:4個字節
    int num;    佔用空間:4個字節
    short x;    佔用空間:8個字節
    double xx; 佔用空間:8個字節
};
struct name1=16個字節           struct name2=24個字節
想不明白了,爲什麼會如此大的差異!!
大家幫幫忙,解釋一下!!!謝謝大家了!
回答1:
這裏必須指出的是你的機器是32位的,而不是SUN SPARC那樣的64位機器。
1. char,short, int,double 各自佔多少字節
這個很容易得到,他們分別是 1, 2,4,8
通過下面的語句可以確定在你的系統下是多少,一般 intel 32 位是上面提到的結果
printf("sizeof(char)=%d/n",sizeof(char))
...
2. 如何對齊字節
32位機器就是按照每32位來對齊,即4字節。
對於struct name1 內部數據的字節數分別是1,2,4,8 按照4字節爲單位進行擺放得到 (1,2)(4)(4)(4) //注意1+2不足4字節,所以放在一起,8字節的double 用2個字節表示。每個括號是4字節,所以得到4*4=16 字節。
對於struct name2 內部數據的字節數分別是1,4,2,8,按照4字節爲單位擺放得到(1)(4)(2)(4)(4),注意1,4不能放在相鄰的一起,因爲大於4字節了,所 以1字節的char獨佔4字節,同樣short 的2 字節也必須佔用4個字節。所以得到5*4 = 20 字節
由此得出 struct name2 佔用20個字節, 你給出的24字節有出入。
請使用printf("sizeof(struct name2)=%d",sizeof(struct name2)),檢查一下。
回答2:
比如name1,struct中佔用最多空間的類型是double,它佔用8個字節,所以默認按照8個字節來進 行對齊(即以8個字節爲一個單位),雖然str x name一共佔用了(1+2+4)7個字節的空間,但是由於下一個類型xx要佔用8個字節的空間,所以xx只能從下一個8字節位置開始,因此一共佔用16 個字節。
對於name2,佔用空間最多的還是double型,所以默認還是按照8個字節來進行對齊,雖然str num一共佔用了(1+4)5個字節的空間,但是下一個類型要佔用2個字節,這時看起來好像可以放到上面的空間裏面,但是由於數據默認是從自己所佔用空間 的整數倍的位置進行對齊,所以num其實是從第一個8字節的第4個字節(從0開始計數)處開始存放,因此x只能放到下一個8字節空間中,同樣道理xx也要 放到再下一個8字節空間中。
回答3:
這個問題應該跟編譯器有關係。
對於結構體中的某一成員item,它相對於結構首地址的實際字節對齊數目X應該滿足
以下規則:
X = min(n, sizeof(item))。 n 是編譯器設定的最大對齊邊界數。
如果n = 8 .
struct name1
    {
    char   str;     偏移爲0 ,從第一個字節位置存儲,佔1個字節
    short x;       偏移爲2 , 從第三個字節位置開始存儲,佔2個字節
    int    num;     偏移爲4, 由於前兩個佔了4個字節,所以從第五個字節開始存儲。佔4個字節
    double xx;     偏移爲8,由於前兩個佔了8個字節,所以從第9個字節開始存儲。佔8個字節
    };
一共佔16個字節。
struct name2
{
    char str; 偏移爲0 ,從第一個字節位置存儲,佔1個字節
    int num;   偏移爲4, 由於前兩個佔了1個字節,所以從第五個字節開始存儲。佔4個字
    short x;   偏移爲2 ,由於前兩個佔了8個字節 從第九個字節位置開始存儲,佔2個字節
    double xx;偏移爲8,由於前兩個佔了10個字節,所以從第17個字節開始存儲。佔8個字節
};
一共佔24個字節。
回答4:
struct name2
{
    char str;    佔用空間:4個字節
    int num;    佔用空間:4個字節
    short x;    佔用空間:8個字節//樓主這裏錯了,應該是2個字節
    double xx; 佔用空間:8個字節
};
struct name2=24個字節 //加起來是18個字節,但是要是sizeof(double)的倍數,所以爲24。
回答5:
struct name1
    {
    char   str;     佔用空間:2個字節
    short x;       佔用空間:2個字節
    int    num;     佔用空間:4個字節
    double xx;     佔用空間:8個字節
    };
就拿這個來說 struct 的相對起始地址爲0x00000000,char是一字節,因此short本來應該的地址是0x00000001,但是由於字節對齊,因此short要在 相對起始地址是2的倍數的位置,因此是在0x00000002的位置上了,然後char+short的地址是4字節,因此int的起始位置是 0x00000004,很符合4的倍數,不用動,double xx則正好符合8的倍數
同理可得下面的
發佈了12 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章