初夏小談:結構體記憶體對齊詳解
記憶體對齊?什麼是記憶體對齊?
對於這個問題我們先來看看這樣的一個結構體(在32位系統下)
typedef struct Stu1
{
char C1;
int num1;
short S1;
}Stu1;
如果我們不知道記憶體對齊或者不清楚記憶體對齊時,我們可能這樣分析:
C1佔一個位元組,num1佔4個位元組,S1佔2個位元組,所以該結構體佔7個位元組。
然後我再VS2017上測試發現得到12個位元組。
這是為什麼呢?這就是由於記憶體對齊導致的。
為什麼會是這樣?在解決這個問題時,先來了解一下記憶體對齊規則 :
1.結構體的第一個成員在結構體變數偏移量為0的地址處。就是結構體地址和第一個元素的地址的值是一樣的,夠明瞭吧。
2.剩下的成員變數要對齊到對齊數的整數倍的地址上。
對齊數聽起來很高大上的樣子,其實一點也不牛逼,就是一個數罷了,就是該結構體的成員佔幾個位元組就是對齊數,每個成員變數都對應一個對齊數。
最大對齊數就是:編譯器的設定對齊數字和結構體中成員佔位元組最大的那個相比取最小的。怎麼樣?它就那麼回事。
3.結構體的總體大小就是對齊數的整數倍。
4.再巢狀結構體的話,別擔心會複雜,你可以這樣做就是把巢狀的結構體中變數最大的對齊數和這個結構體相比對齊到最大對齊數上。結構體的整體大小就是所有最大對齊數的整數倍。
現在來看上面的問題:第一個C1開始是對齊數是1就是“1”,但遇到num時就變成了4,所以是“1*** 1111”,再遇到S1發現它對齊數小於num1所以還是4,所以是這樣儲存:“1*** 1111 11**”。
那麼可不可以既提高效率,又能節省空間,有一個小技巧可以解決這樣的問題。
再來看一個例子:
typedef struct Stu2
{
char C1;
short S1;
int num1;
}Stu2;
和上面的看起來沒什麼區別,但是注意順序變了,我們再走一遍看看它是如何進行記憶體對齊的?
C1開始對齊數是一那就佔一個位元組,“1”,接著S1佔2個位元組那就是“1* 11”,接著num1佔4個位元組那就是:“1*11 1111”。
不過癮?那再來來幾個小試牛刀:
#include<stdio.h>
#include<stdlib.h>
typedef struct student1
{
char name[1020];
double score;
int num;
long long ShenFenZheng;
}student1;
typedef struct student2
{
char name[1020];
int num;
double score;
long long ShenFenZheng;
}student2;
typedef struct student3
{
char name[1024];
double score;
int num;
long long ShenFenZheng;
}student3;
typedef struct Stu1
{
// student4 stu;
char C1;
int num1;
short S1;
}Stu1;
typedef struct Stu2
{
char C1;
short S1;
int num1;
}Stu2;
typedef struct student4
{
Stu1 S1;
char name[1024];
int num;
double score;
long long ShenFenZheng;
}student4;
void Mysizeof()
{
printf("%d\n", sizeof(Stu1));//12
printf("%d\n", sizeof(Stu2));//8
printf("%d\n", sizeof(student3));//1048
printf("%d\n", sizeof(student4));//1056
printf("%d\n", sizeof(student1));//1048
printf("%d\n", sizeof(student2));//1040
}
int main()
{
Mysizeof();
system("pause");
return 0;
}
(二)為什麼要記憶體對齊?
1.移植原因:由於所有的硬體平臺並不是都能訪問任意地址上的任意資料的,有的硬體平臺只能在某些地址處取某些特定型別資料,否則丟擲異常。
2.效能原因:上面已經提及,資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於是為了訪問未對齊的記憶體,處理器需要做記憶體訪問;而對齊的記憶體訪問進需要一次訪問。
結構體的記憶體對齊就是用空間換時間的做為。(魚和熊掌不可兼得,哈哈^_^。。。)
補充說明:
1. 每個結構體變數成員在對齊時先按自己的對齊方式對齊,並可以最小化長度。
2. 對齊後的長度必須是該結構體的最大對齊數的整數倍。
3. 字元陣列char array[1024],它是以char佔的位元組進行對齊,而不是陣列的長度。
4. 我們也可以修改編譯器的預設對齊數。
兩種方式:
1. #pragma pack(n) //n可以設定為需要對齊的數。但注意只能是2的指數倍。
對應的如何取消設定值呢?,在結構體後面加上:#pragma pack() 就可以了,還原為預設值。
2. 另外可以在編譯器我以VS為例:在工程的屬性頁 ---> C/C++ ---> 程式碼生成 ---> 結構成員對齊,可以修改預設值。
珍&原始碼