剛剛完成一個文件的遷移程序,其中遇到了結構體對齊的問題,所以拿出來說說,與各位博友們分享。
我的程序很簡單,就是把之前通過一個結構體fwrite到文件A里的內容讀出,然后轉給另一個結構體保存。程序是簡單,但我擔心的是之前把結構體fwrite到文件A的程序對齊結構體規則是怎樣的?一定要知道它嗎? 當然了,如果那個程序結構體是按照1對齊寫入的,我的程序結構體是按照4對齊讀入,那不就糟了!
這里我引入結構體對齊的概念,也可以說是內存對齊了。為什么要內存對齊呢,就是方便CPU尋址了,具體原因大家要參考計算機體系結構了。先看一個內存對齊的例子:
struct example1{
char a;
double b;
long l;
};
struct example2{
char a;
long l;
double b;
};
大家算算結構體大小,初次接觸的博友可能對答案有點驚訝,VC編譯, sizeof后結果分別是:24,16。 同樣是的結構體,成員換了順序,大小就不同了。其實內存對齊有個規則,只要知道了,就OK。那么以下5點是關鍵
1. 內存對齊與編譯器設置有關,首先要搞清編譯器這個默認值是多少
2. 如果不想編譯器默認的話,可以通過#pragma pack(n)來指定按照n對齊
3. 每個結構體變量對齊,如果對齊參數n(編譯器默認或者通過pragma指定)大于該變量所占字節數(m),那么就按照m對齊,內存偏移后的地址是m的倍數,否則是按照n對齊,內存偏移后的地址是n的倍數。也就是最小化長度規則
4. 結構體總大小: 對齊后的長度必須是成員中最大的對齊參數的整數倍。最大對齊參數是從第三步得到的。
5. 補充:如果結構體A中還要結構體B,那么B的對齊方式是選它里面最長的成員的對齊方式
所以計算結構體大小要走三步,首先確定是當前程序按照幾對齊(參照1,2點),接著計算每個結構體變量的大小和偏移(參照3,5),最后計算結構體總大小(參照4)。
先算算example1吧,假設編譯器是以16對齊的
1.確定按照幾對齊: 16;
2.確定每個成員的偏移:a 占一個字節,16>1, 按照1對齊,起始位置0,0%1 = 0,那么a就存在0位置;b占8個字節,16>8,按照8對齊,起始位置就不能是1了,因為要按照8對齊,所以最近的偏移起始位置是8, 8%8 =0, 那么b就存在位置8-15的位置;l占4個字節,16>4,按照4對齊,起始位置16, 16%4=0,那么l就存在位置16-19的位置。所以結構體從0到19一共占用20個字節
3.結構體總大小:成員中最大的對齊參數是b的8對齊,所以20%8!=0, 24剛好。
真的很搞!同理計算example2應該是16;
再舉個結構體嵌套的例子吧,
#pragma pack(push)
#pragma pack(8)
struct test1{
int a;
char b;
int c[20]
long l;
} ;
struct test2{
char a1;
char a2;
struct test1 t1;
double b1;
}
#pragma pack(pop)
先計算test1, 8對齊,a占用0-3,b占用4,c占用8-87,l占用88-91,一共92個字節。成員中最大的對齊參數是int了92%4=0;
再計算test2, a1z占用0,a2占用1,t1呢,4 % 4 (test1里面最長的成員的對齊方式) = 0, 4-95,b1占96到103;一共104個字節,成員中最大的對齊參數是double了104%8=0; 所以是104.
那關于我文章開頭提到的那個文件轉換,我現在只要知道原始程序是按照什么對齊的,然后在新程序中指定按照幾對齊就可以了,哈哈!
擠時間寫的,有的地方有遺漏,請各位指正!