文章目錄
- 結構體記憶體對齊規則
- 結構體大小計算 - 三步曲
- 為什么存在記憶體對齊?
- 設計結構體時的技巧
- 修改默認對齊數
結構體記憶體對齊規則
我們知道,整型變數有自己的大小,浮點型變數有自己的大小,陣列也有自己的大小,那么結構體有沒有自己的大小呢?
回答是肯定的,結構體也有自己的大小,但是結構體的大小并不是簡單地將每個結構體成員的大小相加就能得到,
結構體的大小計算遵循結構體的對齊規則:
- 第一個成員在與結構體變數偏移量為0的地址處,(即結構體的首地址處,即對齊到0處)
- 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處,
- 結構體的總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍,
- 如果嵌套了結構體,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍,
對齊數 = 該結構體成員變數自身的大小與編譯器默認的一個對齊數的較小值,
注:VS中的默認對齊數為8,Linux的默認對齊數為4,不是所有編譯器都有默認對齊數,當編譯器沒有默認對齊數的時候,成員變數的大小就是該成員的對齊數,
結構體大小計算 - 三步曲
知道了結構體記憶體對齊規則,我們就可以計算結構體的大小了,計算結構體的大小可分為三個步驟,我們拿下面這個結構體舉例:
struct S
{
double d;
char c;
int i;
};
第一步:找出每個成員變數的大小將其與編譯器的默認對齊數相比較,取其較小值為該成員變數的對齊數,

注:我使用的是VS編譯器,故默認對齊數為8,
第二步:根據每個成員對應的對齊數畫出它們在記憶體中的相對位置,

第三步:通過最大對齊數決定最終該結構體的大小,
通過圖我們可以知道,綠色部分(double d成員占用)+紅色部分(char c成員占用)+紫色部分(int i成員占用)+紅色與紫色之間的白色部分(浪費掉了)總共占用了16個位元組的記憶體空間,
我們需要將它們總共占用的記憶體空間(16)與結構體成員的最大對齊數(8)相比較,結構體的總大小為最大對齊數的整數倍,此時16正好是8的整數倍,所以該結構體在VS編譯器下的大小就16個位元組,即創建一個該型別的結構體變數,記憶體需為其開辟16個位元組的記憶體空間,
注意:大多數情況下,成員變數已經占用的總位元組個數并不一定正好為其成員變數中的最大對齊數的整數倍,這時我們需要將其擴大為最大對齊數的整數倍,
為什么存在記憶體對齊?
平臺原因(移植原因): 不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些平臺只能在某些地址處取得某些特定型別的資料,否則拋出硬體例外,
比如,當一個平臺要取一個整型資料時只能在地址為4的倍數的位置取得,那么這時就需要記憶體對齊,否則無法訪問到該整型資料,
性能原因: 資料結構(尤其是堆疊)應該盡可能的在自然邊界上對齊,原因在于,為了訪問未對齊記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需一次,
在畫圖時可能有博友會想,記憶體這么重要,在進行記憶體對齊的時候怎么還有記憶體被白白浪費掉呢?
現在看來,其實結構體的記憶體對齊是拿空間來換取時間的做法,
設計結構體時的技巧
其實在我們設計結構體的時候,如果結構體成員的順序設計得合理的話,是可以避免不必要的記憶體消耗的,
兩個結構體的成員變數相同,但是成員變數的順序不同,可能就會出現結構體的大小不同的情況:
struct S1
{
char a;
char b;
int c;
};//結構體1
struct S2
{
char a;
int c;
char b;
};//結構體2
我們可以看到,結構體1和結構體2的成員變數一模一樣,可是當我們按照記憶體對齊規則來計算兩個結構體的大小的時候,會發現兩個結構體的大小不一樣,在VS編譯器下第一個結構體大小為8,第二個結構體大小為12,
可以見得,結構體成員變數的順序不同,可能會造成記憶體不必要的損失,將占用空間小的成員盡量集中在一起,可以有效地避免記憶體不必要的浪費,
修改默認對齊數
要修改編譯器的默認對齊數,我們需要借助于以下預處理命令:
#pragma pack()
如果在該預處理命令的括號內填上數字,那么默認對齊數將會被改為對應數字;如果只使用該預處理命令,不在括號內填寫數字,那么會恢復為編譯器默認的對齊數,
#include <stdio.h>
#pragma pack(4)//設定默認對齊數為4
struct S1
{
char a;//1/4->1
int b;//4/4->4
char c;//1/4->1
};//12
#pragma pack()//取消設定的默認對齊數,還原為默認
#pragma pack(1)//設定默認對齊數為1
struct S2
{
char a;//1/1->1
int b;//4/1->1
char c;//1/1->1
};//6
#pragma pack()//取消設定的默認對齊數,還原為默認
int main()
{
printf("%d\n", sizeof(struct S1));//列印結果為12
printf("%d\n", sizeof(struct S2));//列印結果為6
return 0;
}
于是,當結構體的對齊方式不合適的時候,我們可以自己更改默認對齊數,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/265962.html
標籤:其他
