这个结构体对齐输出有意思
這個題目是我在群里看到大家討論的,既然是討論的了,那我就拿出來說說,因為筆試面試的時候,可能就會遇到這樣的題目。
實例代碼
#include?"stdio.h" #include?"stdint.h"struct?Obj?{char?a;?//1uint32_t?b;//4uint8_t?c;//1uint64_t?d[0];//8 };int?main() {struct?Obj?Op;printf("%d?%d\n",sizeof(Op),sizeof(Op.d));return?(0); }?程序輸出
16?0-------------------------------- Process?exited?after?0.03048?seconds?with?return?value?0 請按任意鍵繼續.?.?.這里比較令我們疑惑的是,d 的大小明明是 0,為甚結構體的大小會是 16呢?
調戲一下
看看下面代碼的輸出
#include?"stdio.h" #include?"stdint.h"#define?PRINT_D(intValue)?????printf(#intValue"?is?%d\n",?(intValue)); #define?OFFSET(struct,member)??((char?*)&((struct?*)0)->member?-?(char?*)0)#pragma?pack(1)struct?Obj?{char?a;?uint32_t?b;uint8_t?c;uint64_t?d[0]; };int?main() {struct?Obj?Op;PRINT_D(OFFSET(struct?Obj,a));?PRINT_D(OFFSET(struct?Obj,b));?PRINT_D(OFFSET(struct?Obj,c));?PRINT_D(OFFSET(struct?Obj,d));?printf("%d?%d\n",sizeof(Op),sizeof(Op.d));return?(0); }?程序輸出
OFFSET(struct?Obj,a)?is?0 OFFSET(struct?Obj,b)?is?1 OFFSET(struct?Obj,c)?is?5 OFFSET(struct?Obj,d)?is?6 6?0-------------------------------- Process?exited?after?0.0108?seconds?with?return?value?0 請按任意鍵繼續.?.?.這里的輸出剛好是我們的結構體里內容的大小
解釋下這段調試代碼的作用
#define?PRINT_D(intValue)?????printf(#intValue"?is?%d\n",?(intValue)); #define?OFFSET(struct,member)??((char?*)&((struct?*)0)->member?-?(char?*)0)前面哪個比較簡單了,就是使用 「#」這個符號把字符串帶過來打印。下面的OFFSET 比較有意思,先是把 0 這個地址強制轉換成我們需要的strunct ,然后呢,再讀取地址減去0地址,這樣就可以得出它的偏移大小了。
調戲一下2
我們改一下代碼
#include?"stdio.h" #include?"stdint.h"#define?PRINT_D(intValue)?????printf(#intValue"?is?%d\n",?(intValue)); #define?OFFSET(struct,member)??((char?*)&((struct?*)0)->member?-?(char?*)0)#pragma?pack(4)struct?Obj?{char?a;?uint32_t?b;uint8_t?c;uint64_t?d[0]; };int?main() {struct?Obj?Op;PRINT_D(OFFSET(struct?Obj,a));?PRINT_D(OFFSET(struct?Obj,b));?PRINT_D(OFFSET(struct?Obj,c));?PRINT_D(OFFSET(struct?Obj,d));?printf("%d?%d\n",sizeof(Op),sizeof(Op.d));return?(0); }?程序輸出
OFFSET(struct?Obj,a)?is?0 OFFSET(struct?Obj,b)?is?4 OFFSET(struct?Obj,c)?is?8 OFFSET(struct?Obj,d)?is?12 12?0-------------------------------- Process?exited?after?0.01165?seconds?with?return?value?0 請按任意鍵繼續.?.?.調戲代碼3
#include?"stdio.h" #include?"stdint.h"#define?PRINT_D(intValue)?????printf(#intValue"?is?%d\n",?(intValue)); #define?OFFSET(struct,member)??((char?*)&((struct?*)0)->member?-?(char?*)0)#pragma?pack(8)struct?Obj?{char?a;?uint32_t?b;uint8_t?c;uint64_t?d[0]; };int?main() {struct?Obj?Op;PRINT_D(OFFSET(struct?Obj,a));?PRINT_D(OFFSET(struct?Obj,b));?PRINT_D(OFFSET(struct?Obj,c));?PRINT_D(OFFSET(struct?Obj,d));?printf("%d?%d\n",sizeof(Op),sizeof(Op.d));return?(0); }?程序輸出
OFFSET(struct?Obj,a)?is?0 OFFSET(struct?Obj,b)?is?4 OFFSET(struct?Obj,c)?is?8 OFFSET(struct?Obj,d)?is?16 16?0-------------------------------- Process?exited?after?0.01219?seconds?with?return?value?0 請按任意鍵繼續.?.?.結構體對齊大小的方式,這個背下,不背下就存下
原則A:struct或者union的成員,第一個成員在偏移0的位置,之后的每個成員的起始位置必須是當前成員大小的整數倍;
原則B:如果結構體A含有結構體成員B,那么B的起始位置必須是B中最大元素大小整數倍地址;
原則C:結構體的總大小,必須是內部最大成員的整數倍;
這幾個原則是在 沒有 #pragma pack 的時候才起作用的,有了 #pragma pack,就按照 #pragma pack 的方式去對齊。
分析一下
基于上面的實驗和理論,我們可以知道,這個筆試題的輸出結果是因為 uint64_t d[] 這個搞鬼了,就是因為這個搞鬼了,我們結構體的最終大小才是 16。因為 uint64_t d 是 8個字節,這樣結構體就是以 8 字節的方式對齊了。
雖然d 的不占用內存的,但是這個家伙的存在讓結構體的對齊方式產生了改變,就是這么神奇。
為了驗證我們的想法,我們改下程序
實例代碼
#include?"stdio.h" #include?"stdint.h"#define?PRINT_D(intValue)?????printf(#intValue"?is?%d\n",?(intValue)); #define?OFFSET(struct,member)??((char?*)&((struct?*)0)->member?-?(char?*)0)struct?Obj?{char?a;?uint32_t?b;uint8_t?c;uint32_t?d[0]; };int?main() {struct?Obj?Op;PRINT_D(OFFSET(struct?Obj,a));?PRINT_D(OFFSET(struct?Obj,b));?PRINT_D(OFFSET(struct?Obj,c));?PRINT_D(OFFSET(struct?Obj,d));?printf("%d?%d\n",sizeof(Op),sizeof(Op.d));return?(0); }?程序輸出
OFFSET(struct?Obj,a)?is?0 OFFSET(struct?Obj,b)?is?4 OFFSET(struct?Obj,c)?is?8 OFFSET(struct?Obj,d)?is?12 12?0-------------------------------- Process?exited?after?0.01002?seconds?with?return?value?0 請按任意鍵繼續.?.?.看到沒?
看到沒?
看到沒?
這樣之后,程序的輸出就是 12 了,也就是說,現在的程序是按照 4字節對齊方式來對齊了。
? 推薦閱讀:
? 專輯|Linux文章匯總
? 專輯|程序人生
? 專輯|C語言
嵌入式Linux
微信掃描二維碼,關注我的公眾號?
總結
以上是生活随笔為你收集整理的这个结构体对齐输出有意思的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这两天,我们还没毕业
- 下一篇: C语言、嵌入式重点知识:回调函数