1. 程式人生 > 其它 >結構體記憶體對齊(C語言)

結構體記憶體對齊(C語言)

結構體記憶體對齊規則:

1.第一個成員在與結構體變數偏移量為0 的地址處。

2.其他成員變數要對齊到某個數字(對其數)的整數倍的地址處。

對齊數 = 編譯器預設的一個對齊數 與 該成員大小的 較小值。

vs 中預設的值為 8

3.結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍。

4.如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(包含巢狀結構體的對齊數)的整數倍。

實際應用

#include<stdio.h>

struct s1
{
    char c1;
    int a;
    char c2;
};

struct s2 { char c1; char c2; int a; }; int main() { struct s1 s1 = {0}; printf("%d\n",sizeof(s1)); struct s2 s2 = {0}; printf("%d\n",sizeof(s2)); return 0; }

這段程式碼輸出的結果為:

12

8

下面我們來逐一分析:

s1

c1為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。

a為int型,佔四個位元組,對齊數是4,對齊到4的整數倍的地址處,即偏移量為4開始的地址空間。

c2為char型,佔一個位元組,對齊數是1,對齊到1的整數倍的地址處,即偏移量為8開始的地址空間。

結構體總大小為最大對齊數的整數倍,所以為對齊數4的整數倍,現在已經用了9個位元組的空間,那麼總大小就是12個位元組空間。

所以輸出結果是12。

s2

c1為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。

c2為char型,佔一個位元組,對齊數是1,對齊到1的整數倍的地址處,即偏移量為1開始的地址空間。

a為int型,佔四個位元組,對齊數是4,對齊到4的整數倍的地址處,即偏移量為4開始的地址空間。

結構體總大小為最大對齊數的整數倍,所以為對齊數4的整數倍,現在已經用了8個位元組的空間,那麼總大小就是8個位元組空間。

所以輸出結果是8。

結構體的巢狀使用情況

#include<stdio.h>

struct s3
{
    double d;
    char c;
    int i;
};

struct s4
{
    char a;
    struct s3 s3;
    double b;
};

int main()
{
    struct s4 s4;
    printf("%d\n",sizeof(s4));
    return 0;
}

結果為:32

由以上方法容易得出struct S3佔16個位元組,最大對齊數為:8。

a為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。

第二個成員是結構體巢狀使用,結構體S3變數s3,剛才已經得出佔16個位元組,最大對齊數是8,所以對齊到偏移量為8的地址空間。

b為double型,佔八個位元組,對齊數是8,對齊到8的整數倍的地址處,即偏移量為24開始的地址空間。

結構體總大小為最大對齊數的整數倍,所以為對齊數8的整數倍,現在已經用了32個位元組的空間,那麼總大小就是32個位元組空間。

所以輸出結果是32。

s3

s4

為什麼存在記憶體對齊:

1.平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否者丟擲硬體異常。

2.效能原因:資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要做兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。

總體來說:

結構體的記憶體對齊是拿空間來換取時間的做法。

那在設計結構體的時候,我們既要滿足對齊,又要節省空間,如何做到:

讓佔用空間小的成員儘量集中在一起。

struct s1
{
    char c1;
    int a;
    char c2;
};

struct s2
{
    char c1;
    char c2;
    int a;
};

s1 和 s2 型別的成員一模一樣,但是s1和s2所佔的空間的大小有了一些區別。

設定預設對齊數:

#pragma pack(8)//設定預設對齊數為8
struct s1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消設定的預設對齊數,還原為預設