以下的內容C++中也一樣。
結構體中的成員可以是不同的數據類型,成員按照定義時的順序依次存儲在連續的內存空間。和數組不一樣的是,結構體的大小不是所有成員大小簡單的相加,需要考慮到系統在存儲結構體變量時的地址對齊問題。
一、為何要內存對齊
因為處理器讀寫數據,並不是以字節為單位,而是以塊(2,4,8,16字節)為單位進行的。如果不進行對齊,那么本來只需要一次進行的訪問,可能需要好幾次才能完成,並且還要進行額外的merger或者數據分離。導致效率低下。更嚴重地,有些架構的CPU會因為不允許訪問unaligned address,就會報錯.
二、內存對齊
結構體的內存布局依賴於CPU、操作系統、編譯器及編譯時的對齊選項。結構體內部成員的對齊要求,結構體本身的對齊要求。
(一)成員對齊。對於結構體內部成員,通常會有這樣的規定:各成員變量存放的起始地址相對於結構的起始地址的偏移量必須為該變量的類型所占用的字節數的倍數。(偏移量是size的倍數)
(二)整個結構體的對齊需求。要求結構體至少是其中的那個最大的元素大小的整數倍。因為有時候我們使用的是結構體數組,所以結構體的大小還得保證結構體數組中各個結構體滿足對齊要求,同時獨立的結構體與結構體數組中單個結構體的大小應當是一致的。(結構體size是max的倍數)
(三)編譯器的對齊指令。VC 中提供了#pragma pack(n)來設定變量以n字節對齊方式。n字節對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大於等於該變量所占用的字節數,那么偏 移量必須滿足默認的對齊方式,第二、如果n小於該變量的類型所占用的字節數,那么偏移量為n的倍數,不用滿足默認的對齊方式。結構的總大小也有個約束條 件,分下面兩種情況:如果n大於所有成員變量類型所占用的字節數,那么結構的總大小必須為占用空間最大的變量占用的空間數的倍數。(成員對齊:min(default, n), 結構體對齊:n>max按默認結構體對齊,小於等於不知道--)
1. 典型例子
struct MyStruct { char dda; //偏移量為0,滿足對齊方式,dda占用1個字節; double dda1;//下一個可用的地址的偏移量為1,不是sizeof(double)=8的倍數,需要補足7個字節才能使偏移量變為8(滿足對齊方式), //因此VC自動填充7個字節,dda1存放在偏移量為8 //的地址上,它占用8個字節。 int type; //下一個可用的地址的偏移量為16,是sizeof(int)=4的倍數,滿足int的對齊方式, //所以不需要VC自動填充,type存 //放在偏移量為16的地址上,它占用4個字節。 }; //所有成員變量都分配了空間,空間總的大小為1+7+8+4=20,不是結構的節邊界數(即結構中占用最大空間的類型所占用的字節數sizeof(double)=8)的倍數, // 所以需要填充4個字節,以滿足結構的大小為sizeof(double)=8的倍數。
所以該結構總的大小為:sizeof(MyStruc)為1+7+8+4+4=24。其中總的有7+4=11個字節是VC自動填充的,沒有放任何有意義的東西。
2. 成員變量含結構體
#include<stdio.h> typedef struct node { char a; double b; int c; }Node; typedef struct node2 { char a; Node b; // 只需把b展開,且b的起始地址必須是b中最大元素size的倍數 }Node2; int main() { Node t1; printf("%d\n", sizeof(t1)); Node2 t2; printf("%d\n", sizeof(t2)); }
所以t2總的大小為(1+7)+(1+7+8+4) + 對齊 = 28 + 4 = 32.
3. 使用編譯器的對齊指令
#pragma pack (2) /*指定按2字節對齊*/
參考鏈接: