C++ 结构体内存对齐
剛出學(xué)校那會(huì),出去找實(shí)習(xí)工作,面試答題被問(wèn)到結(jié)構(gòu)體內(nèi)存大小的問(wèn)題,雖然自己以前接觸過(guò),但是也還是答錯(cuò)了,最終面試的這份工作黃了。一年后的最近,還是在找工作的路上,面試了一家游戲公司,其中也是答題環(huán)節(jié)有一道題被問(wèn)到結(jié)構(gòu)體的內(nèi)存大小,也還是答錯(cuò)了,最后面試也還是黃了。
到了現(xiàn)在自己才醒悟過(guò)來(lái),這些知識(shí)點(diǎn)是有多么的重要,所以咬緊牙,把這個(gè)內(nèi)容的知識(shí)點(diǎn)啃下來(lái),寫(xiě)下這篇博客記錄下來(lái)!
先看一段簡(jiǎn)單的程序
#include <iostream>using namespace std;typedef struct A {char c;int i; };typedef struct B {char c;int i;double d; };typedef struct C {char c;int i;double d;char c1; };int main(void) {cout << "struct A size = " << sizeof(struct A) << endl;cout << "struct B size = " << sizeof(struct B) << endl;cout << "struct C size = " << sizeof(struct C) << endl;return 0; }如果不運(yùn)行,那么大家知道結(jié)構(gòu)體A、B、C所占用的內(nèi)存大小是多少嗎?
運(yùn)行截圖:
以上輸出的結(jié)果并非實(shí)際成員占用的字節(jié)數(shù),這就是結(jié)構(gòu)體的內(nèi)存對(duì)齊!
按照我們正常的邏輯,結(jié)構(gòu)體A的內(nèi)存大小應(yīng)該是5個(gè)字節(jié),結(jié)構(gòu)體B的內(nèi)存大小應(yīng)該是13個(gè)字節(jié),結(jié)構(gòu)體C的內(nèi)存大小應(yīng)該是14字節(jié)才對(duì)!
但是為什么是8、16和24呢?將在下面進(jìn)行講解。
結(jié)構(gòu)體內(nèi)存對(duì)齊原因
平臺(tái)原因(移植原因):
“不是所有的硬件平臺(tái)都能訪問(wèn)任意地址上的任意數(shù)據(jù);某些硬件平臺(tái)只能在某些特定地址處取某些特定的數(shù)據(jù),否則就會(huì)拋出硬件異常”。也就是說(shuō)在計(jì)算機(jī)在內(nèi)存讀取數(shù)據(jù)時(shí),只能在規(guī)定的地址處讀數(shù)據(jù),而不是內(nèi)存中任意地址都是可以讀取的。
效率原因:
正是由于只能在特定的地址處讀取數(shù)據(jù),所以在訪問(wèn)一些數(shù)據(jù)時(shí),對(duì)于訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要進(jìn)行兩次訪問(wèn);而對(duì)于對(duì)齊的內(nèi)存,只需要訪問(wèn)一次就可以。 其實(shí)這是一種以空間換時(shí)間的做法,但這種做法是值得的。
結(jié)構(gòu)體對(duì)齊規(guī)則
第一個(gè)成員在結(jié)構(gòu)體變量偏移量為0 的地址處,也就是第一個(gè)成員必須從頭開(kāi)始。
其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處。對(duì)齊數(shù)為編譯器默認(rèn)的一個(gè) 對(duì)齊數(shù)與該成員大小中的較小值。vs中默認(rèn)值是8 Linux默認(rèn)值為4(可以通過(guò)#pragma pack (N) 修改,使用#pragma pack(show) 可以查看對(duì)齊值),但修改時(shí)N的取 值只能設(shè)置成1, 2,4,8,16.
結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)的整數(shù)倍。(每個(gè)成員變量都有自己的對(duì)齊數(shù))
如果嵌套結(jié)構(gòu)體,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是 所有最大對(duì)齊數(shù)(包含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。(具體詳情,請(qǐng)看下面代碼結(jié)構(gòu)體G)
結(jié)構(gòu)體對(duì)齊的規(guī)則都是依據(jù)上面四條規(guī)則進(jìn)行的!
結(jié)構(gòu)體對(duì)齊示例
typedef struct A {char c;int i; };根據(jù)第二和第三條規(guī)則,VS中最大默認(rèn)對(duì)齊數(shù)是8,結(jié)構(gòu)體中最小的是int類(lèi)型的4,8 < 4,所以結(jié)構(gòu)體A的對(duì)齊數(shù)是4.
所以即使char類(lèi)型占一個(gè)字節(jié),但是他在內(nèi)存中也會(huì)占用4個(gè)字節(jié),來(lái)對(duì)齊4字節(jié)數(shù),這就是所謂的,犧牲空間來(lái)?yè)Q取時(shí)間!
后面的也是一樣的道理!
如果是這樣的結(jié)構(gòu)體,那么他們的內(nèi)存將會(huì)是多少呢?
typedef struct D {char c;int i;double d;char c1;int b;char c2; };typedef struct E {int i;double d;char c;double d2; };typedef struct F {int i;double d;char c;double d2;int i2;int i3;char c2;char c3;char c4;int i4; };typedef struct G {int i;double d;struct E e;char c;double d2; };
下面是他們的內(nèi)存關(guān)系圖:
上述四張圖基本涵蓋了所有情況,希望這四張圖大家能看明白是怎么個(gè)回事!
我本人的理解:
以它為例,
首先我會(huì)先找到結(jié)構(gòu)體中最大的變量字節(jié)大小(內(nèi)部結(jié)構(gòu)體除外),然后使用加法原則向其對(duì)齊。(結(jié)構(gòu)體D的對(duì)齊數(shù)是8)
c 是1個(gè)字節(jié),i是4個(gè)字節(jié),c + i 等于5字節(jié),為了符合內(nèi)存對(duì)齊的原則,他倆占用最前方8個(gè)字節(jié)的內(nèi)存,當(dāng)然c是占4個(gè)字節(jié),i也是占4個(gè)字節(jié),這樣4 + 4 等于8,才符合內(nèi)存對(duì)齊。
d是8個(gè)字節(jié),緊接著后面存儲(chǔ)8個(gè)字節(jié)。
c1是一個(gè)字節(jié),b是4個(gè)字節(jié),計(jì)算規(guī)格和上面一樣,占用8個(gè)字節(jié)。
最后剩余一個(gè)c2是一個(gè)字節(jié),當(dāng)然為了符合內(nèi)存對(duì)齊的原則他在結(jié)構(gòu)體中是占8個(gè)字節(jié)的。
所以最后計(jì)算出內(nèi)存大小為:
(c + i) == 8(字節(jié))
d == 8(字節(jié))
(c1 + b) == 8(字節(jié))
c2 == 8(字節(jié))
8 + 8 + 8 + 8 == 32(字節(jié))
相信再配合下圖,一定可以理解是什么原理的:
總結(jié)
結(jié)構(gòu)體內(nèi)存對(duì)齊這是比較底層的知識(shí)點(diǎn)了,當(dāng)然這也是我們程序員需要掌握的,特別是服務(wù)器開(kāi)發(fā)人員。
對(duì)齊時(shí)需要注意對(duì)齊數(shù)!
總結(jié)
以上是生活随笔為你收集整理的C++ 结构体内存对齐的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 需要换个环境
- 下一篇: s3c2440移植MQTT