1. 程式人生 > >C語言之記憶體位元組對齊

C語言之記憶體位元組對齊

1.結構體的記憶體大小

比如下面這段程式碼:

#include<stdio.h>
#include<stdlib.h>
struct arr
{
    int a;
    int b;
};
int main()
{

    printf("%d", sizeof(struct arr));
    system("pause");
    return 0;
}

我們可以知道這最終輸出為8,當然,struct的大小是每個成員大小的和,所以4+4就是8。

如果你按照上面這樣理解下面的這一段程式碼:

#include<stdio.h>
#include<stdlib.h>
struct arr { int a; char c; int b; char d; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }

你認為輸出會是多少呢?是上面那樣4+1+4+1=10嗎?

答案是輸出為16,這裡,就牽扯到了一個叫做記憶體位元組對齊的問題
上述結構體,在記憶體中的儲存方式,其實就可以這樣理解:
這裡寫圖片描述

採用記憶體對齊的方式,使得我們查詢資訊更加方便,缺陷是浪費了一些空間。

其實位元組對齊的細節和具體編譯器實現相關,但一般而言,滿足下面的三條準則。

  • 位元組對齊的三條準則:

1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;
2) 結構體每個成員相對於結構體首地址的偏移量都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組;例如上面第二個結構體變數的地址空間。
3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組。

所謂最寬基本型別是指像char、short、int、float、double這樣的內建資料型別。“資料寬度”就是指其sizeof的大小。諸如結構體、共用體和陣列等都不是基本資料型別。

對上述三條準則的理解:

#include<stdio.h>
#include<stdlib.h> struct arr { char a; char arr[11]; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }

在這裡我們可以知道,這裡的struct佔12個位元組,12可以整除1,所以結果就是12。在這最寬基本型別為char ,最終的結果為12,是1個的整數倍。

#include<stdio.h>
#include<stdlib.h>
struct arr
{
    short a;
    char arr[11];
};
int main()
{

    printf("%d", sizeof(struct arr));
    system("pause");
    return 0;
}

這裡呢,最寬基本型別是short,佔2個位元組,所以記憶體在儲存時候,將是下面這種情況:
這裡寫圖片描述
這裡我們11+2無法整除,所以,這裡我們多開闢一塊空間,11+2+1整除2。
所以我們可以得到最終的結果就是14。
結果14,是最寬基本型別short的整數倍。

所以,對於最寬基本型別就相當於對記憶體的存查大小的一個限定。所以後續的操作你也可以根據上述的分析一樣分析。

對於第二條的理解:

#include<stdio.h>
#include<stdlib.h>
struct ar
{
    char ch;
    int a;
    short s;
};
int main()
{
    printf("%d\n", sizeof(struct ar));

}

在這裡,進行偏移時,假設結構體的地址為300500,ch地址300200,這時候0可以被1整除,那麼偏移到a的地址就是300204,這時候300504-300500就可以整除4,到s偏移為300208,這時候就可以整除4,所以總共這時候是12個位元組。

二對於下面這種方式:

#include<stdio.h>
#include<stdlib.h>
struct ar
{
    char ch;
    short s;
    int a;
};
int main()
{
    printf("%d\n", sizeof(struct ar));

}

在這裡假設結構體地址300200,那麼首先ch地址為300200,這時候300200-300200可以整除1,接下來偏移,300201不可以整除2,所以再向後偏移到300202,然後一直便宜到300204,300204可以被4整除,所以這裡總共8個位元組。

例:

#include<stdio.h>
#include<stdlib.h>
struct ar
{
    char ch;
    short s;
    double a;
    int num;
    char arr[19];
};
int main()
{
    printf("%d\n", sizeof(struct ar));
    system("pause");

}

這個例子中,我們可以按照上面的方式來進行分析,假設結構體首地址200200,那麼ch就是200200-200200=0,0可以整除1,然後接下來是short,200201-200200不可以整除2,所以接下來,要根據對齊,補齊,所以偏移到200202,然後下面200204-200200不能整除8,所以偏移至200208,接下來200216-200200可以整除4,所以偏移至200216,然後200220-200200整除1,可以整除,偏移至200220,接下來要考慮最寬資料型別,現在所佔的位元組數為8+8+4+19,所以填充一個位元組,最終結果為40個位元組大小。

2.共用體的記憶體對齊

對於共用體,也是一樣的,也遵守結構體三個規則,共用體的大小也必須是最寬基本型別大小整數倍。
如:

#include<stdio.h>
#include<stdlib.h>
union ar
{
    char ch;
    short s;
    double a;

};
int main()
{
    printf("%d\n", sizeof(union ar));
    system("pause");

}

最寬資料型別為double,所以,輸出最終為8。

如:

#include<stdio.h>
#include<stdlib.h>
union ar
{
    int ch;
    char arr[13];


};
int main()
{
    printf("%d\n", sizeof(union ar));
    system("pause");

}

這裡依然,需要最後的結果是最寬資料型別的整數倍,因為最大的變數為arr,長度為13,然後補齊3個位元組,所以最終的結果也就是16。

水平有限,如有錯誤,請大家都指出來。