C語言結構體的對齊原則

C語言結構體的對齊原則

Q:關於結構體的對齊,到底遵循什麼原則?

A:首先先不討論結構體按多少字節對齊,先看看只以1字節對齊的情況:

#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)

#pragma pack(1)

typedef struct
{
    char    sex;
    short   score;
    int     age;
}student;

int main()
{
    PRINT_D(sizeof(student))
    PRINT_D(OFFSET(student,sex))
    PRINT_D(OFFSET(student,score))
    PRINT_D(OFFSET(student,age))
    return 0;
}

輸出:
sizeof(student) is 7
OFFSET(student,sex) is 0
OFFSET(student,score) is 1
OFFSET(student,age) is 3
可以看到,如果按1字節對齊,那麼結構體內部的成員緊密排列,sizeof(char) == 1, sizeof(short) == 2, sizeof(int) == 4.

修改上面的代碼, 去掉#pragma pack語句,代碼如下:

#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)

typedef struct
{
    char    sex;
    short   score;
    int     age;
}student;

int main()
{
    PRINT_D(sizeof(student))
    PRINT_D(OFFSET(student,sex))
    PRINT_D(OFFSET(student,score))
    PRINT_D(OFFSET(student,age))
    return 0;
}

運行結果:
sizeof(student) is 8
OFFSET(student,sex) is 0
OFFSET(student,score) is 2
OFFSET(student,age) is 4

此時,各個成員之間就不像之前那樣緊密排列了,而是有一些縫隙。這裏需要介紹下對齊原則:

此原則是在沒有#pragma pack語句作用時的原則(不同平臺可能會有不同):

原則A:struct或者union的成員,第一個成員在偏移0的位置,之後的每個成員的起始位置必須是當前成員大小的整數倍;

原則B:如果結構體A含有結構體成員B,那麼B的起始位置必須是B中最大元素大小整數倍地址;

原則C:結構體的總大小,必須是內部最大成員的整數倍;

依據上面3個原則,我們來具體分析下: sex在偏移0處,佔1字節;score是short類型,佔2字節,score必須以2的整數倍爲起始位置,所以它的起始位置爲2; age爲int類型,大小爲4字節,它必須以4的整數倍爲起始位置,因爲前面有sex佔1字節,填充的1字節和score佔2字節,地址4已經是4的整數倍,所以age的位置爲4.最後,總大小爲4的倍數,不用繼續填充。

繼續修改上面的代碼,增加#pragma pack語句:

#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct, member)  ((char *)&((struct *)0)->member - (char *)0)

#pragma pack(4)

typedef struct
{
    char    sex;
    short   score;
    int     age;
}student;

int main()
{
    PRINT_D(sizeof(student))
    PRINT_D(OFFSET(student,sex))
    PRINT_D(OFFSET(student,score))
    PRINT_D(OFFSET(student,age))
    return 0;
}

運行結果:
sizeof(student) is 8
OFFSET(student,sex) is 0
OFFSET(student,score) is 2
OFFSET(student,age) is 4

具體分析下:

有了#pragma pack(4)語句後,之前說的原則A和C就不適用了。實際對齊原則是自身對齊值(成員sizeof大小)和指定對齊值(#pragma pack指定的對齊大小)的較小者。依次原則,sex依然偏移爲0, 自身對齊值爲1,指定對齊值爲4,所以實際對齊爲1; score成員自身對齊值爲2,指定對齊值爲4,實際對齊爲2;所以前面的sex後面將填充一個1字節,然後是score的位置,它的偏移爲2;age自身對齊值爲4,指定對齊爲4,所以實際對齊值爲4;前面的sex和score正好佔用4字節,所以age接着存放;它的偏移爲4.

Q:關於位域的問題,空域到底表示什麼?
A:它表示之後的位域從新空間開始。

#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct, member)  ((char *)&((struct *)0)->member - (char *)0)

typedef struct 
{
    int a : 1;
    int b : 3;
    int : 0;
    int d : 2;
}bit_info;

int main()
{
    PRINT_D(sizeof(bit_info))
    return 0;
}
運行結果:
sizeof(bit_info) is 8
bit_info中的a, b佔用4個字節的前4位,到int:0; 時表示此時將填充餘下所有沒有填充的位,即剛剛的4個字節的餘下28位;int d:2; 將從第四個字節開始填充,又會佔用4個字節,所以總大小爲8.


發佈了47 篇原創文章 · 獲贊 62 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章