想要計(jì)算結(jié)構(gòu)體大小,必須先掌握結(jié)構(gòu)體內(nèi)存對(duì)齊規(guī)則:
1.第一個(gè)成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
2.其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處。
3.對(duì)齊數(shù) = 編譯器默認(rèn)的一個(gè)對(duì)齊數(shù) 與 該成員大小的較小值。 windows(32)/VC6.0 中默認(rèn)的值為8, linux(32)/GCC 中的默認(rèn)值為4。
4.結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(每個(gè)成員變量都有一個(gè)對(duì)齊數(shù))的整數(shù)倍。
如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。
練習(xí)1
struct S1{
char c1;
int i;
char c2;
};

c1先存入0地址處,i的對(duì)齊數(shù)為4(int 為4, 默認(rèn)為8,取?。谧兞科茷?的地址處存入(即4處)(這里為什么是4呢?這個(gè)是根據(jù)我們的第二條規(guī)則,從地址為0開始,對(duì)齊數(shù)的整數(shù)倍開始填入),c2對(duì)齊數(shù)為1,在8處存入,共消耗9個(gè)地址;結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(4)的整數(shù)倍,即12。
練習(xí)2
struct S2
{
char c1;
char c2;
int i;
};

c1存入0地址處,c2對(duì)齊數(shù)為1(char為1,默認(rèn)8,取小),在變量偏移為0的地址處存入(即1處),i的對(duì)齊數(shù)為4(int為4,默認(rèn)8,取?。谧兞科茷?的地址處存入(即4處),共消耗8個(gè)地址;結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(4)的整數(shù)倍,即8。
練習(xí)3
struct S3
{
double d;
char c;
int i;
};

d存入0地址處,c對(duì)齊數(shù)為1,存入8處,i對(duì)齊數(shù)為4,存入12處,共消耗16地址;結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(4)的整數(shù)倍,即16。
練習(xí)4-結(jié)構(gòu)體嵌套問題
struct S4
{
char c1;
struct S3 s3;
double d;
};

c1存入0地址處,S3對(duì)齊到其最大對(duì)齊數(shù)的整數(shù)倍處(即8處),依次對(duì)齊d、c、i、d,消耗32個(gè)地址,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(即8)的整數(shù)倍,32恰好是8的整數(shù)倍,所以為32。
2. 為什么存在內(nèi)存對(duì)齊
大部分的參考資料是這樣說的:
平臺(tái)原因(移植原因): 不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
性能原因: 數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。 原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。
總體來說:
結(jié)構(gòu)體的內(nèi)存對(duì)齊是拿空間來換取時(shí)間的做法
那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿足對(duì)齊,又要節(jié)省空間,如何做到:
讓占用空間小的成員盡量集中在一起。
//例如:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
S1和S2類型的成員一模一樣,但是S1和S2所占空間的大小有了一些區(qū)別。
3. 修改默認(rèn)對(duì)齊數(shù)
#include <stdio.h>
#pragma pack(8)//設(shè)置默認(rèn)對(duì)齊數(shù)為8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消設(shè)置的默認(rèn)對(duì)齊數(shù),還原為默認(rèn)
#pragma pack(1)//設(shè)置默認(rèn)對(duì)齊數(shù)為
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消設(shè)置的默認(rèn)對(duì)齊數(shù),還原為默認(rèn)
int main()
{
//輸出的結(jié)果是什么?
printf("%d\n", sizeof(struct S1));? ? ? ? ? //12
printf("%d\n", sizeof(struct S2));? ? ? ? ? //6
return 0;
}
結(jié)論:
結(jié)構(gòu)在對(duì)齊方式不合適的時(shí)候,那么可以自己更改默認(rèn)對(duì)齊數(shù)。