今天又遇到一個(gè)惡搞問(wèn)題,內(nèi)存對(duì)齊 為什么會(huì)出現(xiàn)內(nèi)存對(duì)齊? 效率問(wèn)題,對(duì)于結(jié)構(gòu)體,訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問(wèn);而對(duì)齊的內(nèi)存訪問(wèn)僅需要一次訪問(wèn)。 為什么要考慮內(nèi)存對(duì)齊? 跨平臺(tái),跨語(yǔ)言,跨編譯器的時(shí)候因?yàn)閮?nèi)存對(duì)齊的原因可能造成設(shè)置的數(shù)據(jù)和獲取的數(shù)據(jù)不同。 舉幾個(gè)簡(jiǎn)單的例子,假如您在32位操作系統(tǒng)下將上面的結(jié)構(gòu)體直接以取地址的方式把該結(jié)構(gòu)體寫(xiě)入到一個(gè)文件中,再在64位操作系統(tǒng)下讀取,直接將內(nèi)存付給這個(gè)結(jié)構(gòu)體,就會(huì)出現(xiàn)問(wèn)題。 再比如我使用上面的結(jié)構(gòu)體編譯一個(gè)C++程序,直接將該結(jié)構(gòu)體對(duì)應(yīng)的內(nèi)存發(fā)送給一個(gè)Java程序,Java程序在讀取這段數(shù)據(jù)的時(shí)候就麻煩了。 所以為了避免內(nèi)存對(duì)齊,我建議自己定義數(shù)據(jù),顯式的將結(jié)構(gòu)體中的數(shù)據(jù)按照既定的規(guī)則寫(xiě)到數(shù)據(jù)流中。雖然這樣可以避免內(nèi)存對(duì)齊,但是大家也應(yīng)該了解一下內(nèi)存對(duì)齊。 內(nèi)存對(duì)齊和編譯器有關(guān)。 編譯器有自己的默認(rèn)“對(duì)齊系數(shù)”(也叫對(duì)齊模數(shù))。程序員可以通過(guò)預(yù)編譯命令#pragma VS2008的對(duì)齊系數(shù)默認(rèn)為8。 舉例說(shuō)明: struct { char a; short b; int c; }; int main() { Test test.a = 0x12; test.b = 0x1234; test.c = 0x12345678; int size = sizeof(Test); Test *point = &test; return 0; } 運(yùn)行之后得到: size為8 內(nèi)存排列方式為: 因?yàn)槲业碾娔X的CPU是intel的,所以在內(nèi)存中小端存儲(chǔ),也就是你看到的c是按照byte倒敘存儲(chǔ)的(CPU大小端問(wèn)題將會(huì)在以后的章節(jié)中詳細(xì)說(shuō)明) 但是在中間有一byte的空白內(nèi)存,這就是編譯器內(nèi)存對(duì)齊造成的。 而如果數(shù)據(jù)結(jié)構(gòu)是這樣的(a和c的順序變了): struct { char a; int c; short b; }; 運(yùn)行之后的結(jié)果是: size的值為12 內(nèi)存的排列方式為:
內(nèi)存對(duì)齊的規(guī)則: VS2008默認(rèn)#pragma pack(n)中的n=8 struct { char a; n=8 char=1 , 對(duì)齊起始地址為1的倍數(shù),所以a的起始內(nèi)存地址為0,內(nèi)存區(qū)間[0,0] short b; n=8 short=2, 對(duì)齊起始地址為2的倍數(shù),所以b的起始內(nèi)存地址為2,內(nèi)存區(qū)間[2,3] int c; n=8 int=4, 對(duì)齊起始地址為4的倍數(shù),所以b的起始內(nèi)存地址為4,內(nèi)存區(qū)間[4,7] }; #pragma struct { char a; n=1 char=1 ,對(duì)齊起始地址為1的倍數(shù),所以a的起始內(nèi)存地址為0,內(nèi)存區(qū)間[0,0] short b; n=1 short=2,對(duì)齊起始地址為1的倍數(shù),所以b的起始內(nèi)存地址為1,內(nèi)存區(qū)間[1,2] int c; n=1 int=4, 對(duì)齊起始地址為1的倍數(shù),所以b的起始內(nèi)存地址為3,內(nèi)存區(qū)間[3,6] }; 還存在一種情況: VS2008默認(rèn)#pragma pack(n)中的n=8 struct { double e; char f; }; int size =sizeof(Test); size的值為16 (注:補(bǔ)齊,保證最終大小是8的倍數(shù)) #pragma struct { double e; char f; }; int size =sizeof(Test); size的值為12 (注:補(bǔ)齊,保證最終大小是4的倍數(shù)) 上面兩種情況的內(nèi)存排列是一致的,但是大小是不一樣的。 據(jù)說(shuō)GCC和VS2008是不一樣的,沒(méi)有測(cè)試 下一次有時(shí)間試一下。 對(duì)于內(nèi)存對(duì)齊的問(wèn)題,我的建議就是顯式組織數(shù)據(jù),尤其對(duì)于網(wǎng)絡(luò)數(shù)據(jù)傳輸和讀寫(xiě)文件,不要直接取地址。 |
|
來(lái)自: pgj555 > 《C 學(xué)習(xí)》