天天看點

C語言之結構體記憶體的對齊

C語言之結構體記憶體的對齊

C語言之結構體記憶體的對齊

大綱:

   零.引例

   一.結構體記憶體對齊規則

   二.怎樣計算結構體的大小

   三.設計結構體時要注意的方面

  四.為什麼存在記憶體對齊

   五.修改預設對齊數

C語言之結構體記憶體的對齊

大綱:

   零.引例

   一.結構體記憶體對齊規則

   二.怎樣計算結構體的大小

   三.設計結構體時要注意的方面

   四.為什麼存在記憶體對齊

   五.修改預設對齊數

在前面的章節中,我們談到了C語言中整數以及浮點數的儲存

今天,我們來談一談一些關于結構體記憶體的知識。

我們先來看一個例子:

struct S1
{
    char c1;
    int i;
    char c2;
};      

大家來猜猜這個結構體S1的記憶體是多少?

相信會有人給出 6 的結果,他們或許是這樣想的,兩個 char 類型分别為一個位元組,一個 int 類型又為4個位元組,加起來剛好為6個

但是

結果真是如此嗎?

我們來看看運作結果:

C語言之結構體記憶體的對齊

 為什麼呢,接下來我們就引出正文。

一.結構體記憶體對齊規則

首先,正如引例所示,結構體的記憶體并不是簡簡單單的将結構體各個成員的大小相加。

結構體的大小往往遵循着結構體的對齊規則:

  1. 第一個成員在與結構體變量偏移量為0的位址處。
  2. 其他成員變量要對齊到某個數字(對齊數)的整數倍的位址處。
  3. 結構體總大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。
  4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

這裡要注意的一點就是要解釋一下這個對齊數的概念

對齊數:編譯器預設的一個對齊數 與 該結構體變量成員自身大小的較小值。

  注:

    不是所有的編譯器都有自己預設的對齊數。

    在VS下其預設的對齊數為8

    在linux下的預設值為4

二.怎樣計算結構體的大小

在講計算之前,我們繼續來看一看上面的那個例子:

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stddef.h>

struct S1
{
    char c1;
    int i;
    char c2;
};

int main()
{
    printf("該結構體成員 c1 開始的位置為第 %d 個位元組\n", offsetof(struct S1, c1));
    printf("該結構體成員 i 開始的位置為第 %d 個位元組\n", offsetof(struct S1, i));
    printf("該結構體成員 c2 開始的位置為第 %d 個位元組\n", offsetof(struct S1, c2));
    printf("該結構體所占的記憶體空間為 %d 個位元組\n", sizeof(struct S1));
    return 0;
}      

注:

  宏 offsetof() 可以計算出結構體各成員所相對開始位置的一個偏移量。

  偏移量 :  我們可以了解為把結構體變量第一個成員所儲存的第一個位置置于0,以此遞增 

我們來看看結果:

C語言之結構體記憶體的對齊

 這是為什麼呢?

我們來看看上面所提到的結構體記憶體對齊規則:

C語言之結構體記憶體的對齊

 然後我們來看示意圖:

C語言之結構體記憶體的對齊

此時,關于結構體的大小,我們應該清楚了不少,接下來,我們繼續來看幾道例題:

struct S2
{
    char c1;
    char c2;
    int i;
};
int main()
{
    printf("%d\n", sizeof(struct S2));
    return 0;
 }      

我們看到,S1與S2的差別僅僅隻是調換了一下各成員間的順序,那它所占的記憶體還是剛才的值嗎:

運作結果:

C語言之結構體記憶體的對齊

 我們繼續來分析一下:

C語言之結構體記憶體的對齊

 趁此機會,我們再來鞏固一下:

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

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

它的結果會是多少呢?

C語言之結構體記憶體的對齊

 不知道大家作對了嗎?

解析:

   首先 double 類型占8個位元組

   char 又展覽接下來的一個

   而 int 的對齊數為 4,是以空3個位元組從12開始

   而這個結構體的最大對齊數為8

   是以該結構體占 2*8 = 16個位元組

最後,我們再來看一道嵌套結構體的例題:

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

struct S4
{
    char c1;
    struct S3 s3;
    double d;
};

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

它的結果又為多少呢?

C語言之結構體記憶體的對齊

 解析:

  我們先來看看規則 4: 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,

            結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍

   S3的最大對齊數為 8,它的大小為 16 個位元組

   首先,毋庸置疑的是 char 先放到首位

   接下來因為S3的對齊數為 8,是以S3放在了以位置8開始的16個位元組

   最後是double,對齊數為8,是以放在了24的位置

   最後,該結構體的大小為 4*8 = 32 個位元組

在進行結構體所占大小的計算中,我們又可以得到一個基本程式設計常識:

三.設計結構體時要注意的方面

在我們進行結構體的設計中,我們可以把一些所占空間小的,來湊到一起,提高資源的使用率。

正如上文所提到的例1與例2,結構體成員完全相同,但順序不同,兩個結構體的大小也截然不同

//例1
struct S1
{
    char c1;
    int i;
    char c2;
};

//例2
struct S2
{
    char c1;
    char c2;
    int i;
};

int main()
{
    printf("%d\n", sizeof(struct S1));//12
    printf("%d\n", sizeof(struct S2));//8
    return 0;
}      

四.為什麼存在記憶體對齊

對于這個原因,目前話沒有一種完全正确的答案,但是:

大部分的參考資料都是如是說的:

  1. 平台原因(移植原因):

    不是所有的硬體平台都能通路任意位址上的任意資料的;

    某些硬體平台隻能在某些位址 處取某些特定類型的資料,否則抛出硬體異常。

   2. 性能原因:

     資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。

     原因在于,為了通路未對齊的記憶體,處理器 需要作兩次記憶體通路;而對齊的記憶體通路僅需要一次通路。

對于原因而我們來看一個示意圖:

 

C語言之結構體記憶體的對齊

五.修改預設對齊數

我們通常使用如下的預處理指令來修改編譯器的預設對齊數:

 #pragma pack()                                                                                                                  

 如果()裡面不加數字,則預設為編譯器的預設對齊數

 我們修改的時候,隻需在()裡加一個數字就行

 取消的時候再添加一次 #pragma pack()  即可

注:

  再()裡添加的數字,我們通常加的都是2的多少次方

下面來舉一個執行個體:

#include <stdio.h>
#pragma pack(8)//設定預設對齊數為8
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消設定的預設對齊數,還原為預設
#pragma pack(1)//設定預設對齊數為8
struct S2
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消設定的預設對齊數,還原為預設


int main()
{
    //輸出的結果是什麼?
    printf("%d\n", sizeof(struct S1));//12
    printf("%d\n", sizeof(struct S2));//6
    return 0;
}      

由此可見,我們也可以通過修改預設對齊數來節約  結構體使用的空間。

關于結構體記憶體的講解便到此為止。

筆者水準有限,若有錯誤之處,還望多多指正。

若有想回憶  C語言中整數以及浮點數是怎樣儲存的讀者,可以參見 :C語言之資料在記憶體中的存儲

發表于

2021-03-07 22:08 

guguguhuha 

閱讀(0) 

評論(0) 

編輯 

收藏

繼續閱讀