C語言結構體內存佈局是一個老生常談的問題,網上也看了一些資料,有些說的比較模糊,有些是錯誤的。本人借鑑了前人的文章,通過實踐,總結了一些規則,若有錯誤,但願指正,不勝感激。html
macOS Sierra(10.12.4)
Xcode(8.3)
影響結構體內存佈局有位域和**#pragma pack預處理宏**兩個狀況,下面分狀況說明。數組
結構體字節對齊的細節和具體的編譯器實現相關,但通常來講遵循3個準則:數據結構
sizeof
)所整除。offset
都是成員大小的整數倍,若有須要編譯器會在成員之間加上填充字節。sizeof
爲結構體最寬基本成員大小的整數倍,若有須要編譯器會在最末一個成員以後加上填充字節。下面的demo會爲你們解釋以上規則:函數
struct student {
char name[5];
double weight;
int age;
};
複製代碼
struct school {
short age;
char name[7];
struct student lilei;
};
複製代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
struct student lilei = {"lilei",112.33,20};
printf("size of struct student: %lu\n",sizeof(lilei));
printf("address of student name: %u\n",lilei.name);
printf("address of student weight: %u\n",&lilei.weight);
printf("address of student age: %u\n",&lilei.age);
struct school shengli = {70,"shengli",lilei};
printf("size of struct school: %lu\n",sizeof(shengli));
printf("address of school age: %u\n",&shengli.age);
printf("address of school name: %u\n",shengli.name);
printf("address of school student: %u\n",&shengli.lilei);
}
return 0;
}
複製代碼
struct school
包含 struct student
,因此最寬的基本數據類型爲double
,sizeof(double)
爲8
,1606416152/8 = 200802019
,1606416112/8 = 200802014
)。struct student weight
成員的首地址是1606416160
而不是1606416157
,**但有很重要的一點要注意,這裏的成員爲基本數據類型,不包括char類型數組和結構體成員,char類型數組按1字節對齊,結構體成員存儲的起始位置要從自身內部最大成員大小的整數倍地址開始存儲,**好比struct a
裏有struct b
成員,b
裏有char,int,double
等成員,那b
存儲的起始位置應該從8的整數倍開始。經過struct school
成員內存分佈能夠看出來,school.name
的首地址是1606416114
,而不是1606416119
,school.student
的首地址是1606416128
,能被8
整除,不能被24
整除)。struct student
佔用字節數爲24
而不是20
的緣由)。細心的朋友可能發現&shengli.lilei
(等效於shengli.lilei.name
)的數值並不等於lilei.name
,也就是說struct school shengli
裏的成員struct student lilei
和 struct student lilei
並非指向同一塊內存空間,是值拷貝開闢的一塊新的內存空間,也就是說struct是值類型而不是引用類型數據結構。還有經過內存地址能夠發現兩個結構體變量的內存空間是在內存棧上連續分配的。佈局
結構體使用位域的主要目的是壓縮存儲,位域成員不能單獨被取sizeof
值。C99規定int,unsigned int,bool
能夠做爲位域類型,但編譯器幾乎都對此作了擴展,容許其它類型存在。結構體中含有位域字段,除了要遵循上面3個準則,還要遵循如下4個規則:ui
sizeof
大小,則後一個字段將緊鄰前一個字段存儲,直到不能容納爲止。sizeof
大小,則後一個字段將重新的存儲單元開始,其偏移量爲其類型大小的整數倍。下面的demo會爲你們解釋以上規則:spa
typedef struct A {
char f1:3;
char f2:4;
char f3:5;
char f4:4;
}a;
複製代碼
typedef struct B {
char f1:3;
short f2:13;
}b;
複製代碼
typedef struct C {
char f1:3;
char f2;
char f3:5;
}c;
複製代碼
typedef struct D {
char f1:3;
char :0;
char :4;
char f3:5;
}d;
複製代碼
typedef struct E {
int f1:3;
}e;
複製代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
printf("size of struct A: %lu\n",sizeof(a));
printf("size of struct B: %lu\n",sizeof(b));
printf("size of struct C: %lu\n",sizeof(c));
printf("size of struct D: %lu\n",sizeof(d));
printf("size of struct E: %lu\n",sizeof(e));
}
return 0;
}
複製代碼
struct A
中全部位域成員類型都爲char
,第一個字節只能容納f1
和f2
,f3
從下一個字節開始存儲,第二個字節不能容納f4
,因此f4
也要從下一個字節開始存儲,所以sizeof(a)
結果爲3
。struct B
中位域成員類型不一樣,進行了壓縮,所以sizeof(b)
結果爲2
(不壓縮方式沒有進行驗證,很抱歉)。struct C
中位域成員之間有非位域類型成員,不進行壓縮,所以sizeof(c)
結果爲3。struct D
中有無名位域成員,char f1:3
佔3
個bit
,char :0
移到下1
個字節(移動單位和具體位域類型有關,short
移到下2
個字節,int
移到下4
個字節),char :4
佔4
個bit
,而後不能容納char f3:5
,因此要存到下1
個字節,所以sizeof(d)
結果爲3
。sizeof(e)
結果爲4
,不該該是隻佔用1
個字節麼?不要忘了上面提到的準則3。int a:33
是不被容許的。char:0
表示整個位域向後推一個字節,即該無名位域後的下一個位域從下一個字節開始存放,同理short:0
和int:0
分別表明整個位域向後推兩個和四個字節。當空位域的長度爲具體數值N時(例如 int:2
),該變量僅用來佔N位。編譯器的#pragma pack
指令也是用來調整結構體對齊方式的,不一樣編譯器名稱和用法略有不一樣。使用僞指令#pragma pack(n)
,編譯器將按照n個字節對齊,其取值爲一、二、四、八、16,默認是8,使用僞指令#pragma pack()
,取消自定義字節對齊方式。若是設置#pragma pack(1)
,就是讓結構體沒有填充字節,實現空間「無縫存儲」,這對跨平臺傳輸數據來講是友好和兼容的。結構體中含有#pragma pack預處理宏,除了要遵循上面3個準則,還要遵循如下2個規則:.net
offsetof(item) = min(n, sizeof(item))
#pragma pack(push) //packing stack入棧,設置當前對齊方式
#pragma pack(pop) //packing stack出棧,取消當前對齊方式
#pragma pack(n) //n=1,2,4,8,16保存當前對齊方式,設置按n字節對齊
#pragma pack() //等效於pack(pop)
#pragma pack(push,n)//等效於pack(push) + pack(n)
複製代碼
#pragma pack(4)
typedef struct F {
int f1;
double f2;
char f3;
}f;
#pragma pack()
複製代碼
#pragma pack(16)
typedef struct G {
int f1;
double f2;
char f3;
}g;
複製代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
printf("size of struct D: %lu\n",sizeof(f));
printf("size of struct E: %lu\n",sizeof(g));
}
return 0;
}
複製代碼
struct F
設置的對齊方式爲4
,min(4, sizeof(int)) = 4
,f1
佔4
個字節,偏移量爲0
,min(4, sizeof(double)) = 4
,f2
佔4
個字節,偏移量爲4
,min(4, sizeof(char)) = 1
,f3
佔1
個字節,偏移量爲12
,最後整個結構體知足準則3,sizeof(f) = 16
。struct G
設置的對齊方式爲16
,比結構體中全部成員類型都要大,至關於沒有生效,所以sizeof(f) = 24
。位域和**#pragma pack預處理宏的結構體在遵循3個準則**的前提下,有本身的相應規則也要遵照。結構體成員在排列時數據類型要遵循從小到大排列,這樣能儘量的節省空間。指針
blog.sina.cn/dpool/blog/… c.biancheng.net/cpp/html/46… hubingforever.blog.163.com/blog/static…code
若有任何知識產權、版權問題或理論錯誤,還請指正。
轉載請註明原做者及以上信息。