全攻略有點(diǎn)大放厥詞,嘩眾取寵了,其實(shí)是一些常見(jiàn)的問(wèn)題罷了。但是自認(rèn)為總結(jié)的還算全面一點(diǎn)吧,請(qǐng)大家多多賜教,我只是個(gè)初學(xué)者。

接下來(lái)主要討論四點(diǎn):
一、不涉及位域的內(nèi)存對(duì)齊原則
二、涉及位域的內(nèi)存對(duì)其原則
三、成員變量含有結(jié)構(gòu)體的內(nèi)存對(duì)齊情況
四、要求內(nèi)存對(duì)齊的原因及優(yōu)點(diǎn)

引子:
 1#include <iostream>
 2using namespace std;
 3struct A
 4
 5    char m; 
 6    int n; 
 7}
;
 8int main ()
 9
10    A a; 
11    a.m=1;a.n=2;
12    printf("sizeof(A)=%d\nsizeof(A.m)=%d\nsizeof(A.n)=%d\n",sizeof(a),sizeof(a.m),sizeof(a.n)); 
13    return 0;
14}

預(yù)測(cè)輸出:
sizeof(A)=5
sizeof(A.m)=1
sizeof(A.n)=4
實(shí)際輸出:

分析:
也許該疑問(wèn)了,sizeof(A.m)=1,sizeof(A.n)=4,sizeof(A)不是該1+4=5嗎,怎么是8呢?

這是因?yàn)槌绦騿T眼中的內(nèi)存與處理機(jī)處理內(nèi)存的不一致,請(qǐng)看下圖:
程序員通常認(rèn)為內(nèi)存就是一些列簡(jiǎn)單的字節(jié)數(shù)組,在C語(yǔ)言以及它的衍生語(yǔ)言中,char*被普遍認(rèn)為代表一塊內(nèi)存區(qū)域,即使是Java也用byte[]來(lái)代表原始內(nèi)存如下圖所示:

處理機(jī)在對(duì)內(nèi)存進(jìn)行存取操作時(shí),卻不是以單個(gè)字節(jié)為單位來(lái)進(jìn)行的,它通常是以2 、4、 8 、16大小的字節(jié)塊來(lái)進(jìn)行的。我們稱(chēng)處理器訪問(wèn)內(nèi)存時(shí)一次存取的內(nèi)存大小為內(nèi)存訪問(wèn)粒度”,如下圖所示:


可以看一下內(nèi)存中的情況,確實(shí)是8個(gè)字節(jié):


一、不涉及位域的內(nèi)存對(duì)齊原則
規(guī)則如下,然后來(lái)舉例說(shuō)明:
1)對(duì)結(jié)構(gòu)的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員位于偏移為0的位置,以后每個(gè)數(shù)據(jù)成員的偏移量必須是MIN(#pragma pack()指定的數(shù),此數(shù)據(jù)成員的自身長(zhǎng)度) 的倍數(shù),我們稱(chēng)MIN(#pragma pack()指定的數(shù),此數(shù)據(jù)成員的自身長(zhǎng)度) 為該結(jié)構(gòu)成員的對(duì)齊模數(shù)。
注:程序中通常是不指定#pragma pack()它的,默認(rèn)值為8,從哪里可以看到呢,看下圖(發(fā)現(xiàn)用圖說(shuō)話有時(shí)是直觀方便易懂……):


2)為結(jié)構(gòu)體的一個(gè)成員開(kāi)辟空間之前,編譯器首先檢查預(yù)開(kāi)辟空間的首地址相對(duì)于結(jié)構(gòu)體首地址的偏移是否是本成員的整數(shù)倍,若是,則存放本成員,反之,則在本成員和上一個(gè)成員之間填充一定的字節(jié)(填充字節(jié)為CC,也就是int3中斷),以達(dá)到整數(shù)倍的要求,也就是將預(yù)開(kāi)辟空間的首地址后移幾個(gè)字節(jié)。


3)在數(shù)據(jù)成員完成各自對(duì)齊之后,結(jié)構(gòu)(或聯(lián)合)本身也要進(jìn)行對(duì)齊,對(duì)齊將按照MIN(指定的#pragma pack()的數(shù)值,MAX(結(jié)構(gòu)(或聯(lián)合)數(shù)據(jù)成員長(zhǎng)度))進(jìn)行對(duì)齊。

這部分以這段代碼為例:
 1#include <iostream>
 2using namespace std;
 3struct A
 4
 5    char c; 
 6    int i; 
 7    short s;
 8}
;
 9int main ()
10
11    A a; 
12    a.c=1;a.i=2;a.s=3;
13    printf("sizeof(A)=%d\n",sizeof(A)); 
14    return 0;
15}
這里,我們采用系統(tǒng)默認(rèn)的#pragma pack(8),則
c 的對(duì)齊模數(shù)為:MIN(sizeof(char),8)=MIN(1,8)=1
i  的對(duì)齊模數(shù)為:MIN(sizeof(int),8)=MIN(4,8)=4
s 的對(duì)齊模數(shù)為:MIN(sizeof(short),8)=MIN(2,8)=2
根據(jù)規(guī)則一:

根據(jù)規(guī)則二:
當(dāng)給c開(kāi)辟空間以后,為i開(kāi)辟空間之前,編譯器先檢查與開(kāi)辟空間的首地址,發(fā)現(xiàn)為1,不是i的對(duì)齊模數(shù)的整數(shù)倍,則向后找,一直到地址4時(shí),以4為起始,向后開(kāi)辟四個(gè)字節(jié)大小的空間,而之前的1,2,3這三處則填充 CC ,如下圖:

根據(jù)規(guī)則三:
現(xiàn)在進(jìn)行結(jié)構(gòu)體對(duì)齊,整個(gè)sizeof(A)=10,而MIN(8,MAX(sizeof(c),sizeof(i),sizeof(s)))=4,則sizeof(A)=MIN(4N),并且sizeof(A)>=10,故,sizeof(A)=12.再將10,11空間給填充上CC:

要知道為什么有的是CC,有的是00啊,還有,千萬(wàn)別看到sizeof(A)=12,就把12位置也給填上CC啊

本來(lái)打算寫(xiě)一篇嘮叨完,但我太絮叨了,一個(gè)問(wèn)題說(shuō)了這么多,分開(kāi)寫(xiě)吧……