圖文并茂,一次搞定C語言結(jié)構(gòu)體內(nèi)存對齊!(包含完整源碼)
面試官:你知道C語言的結(jié)構(gòu)體對齊嗎?
應(yīng)聘者:聽說過……平時很少關(guān)注 ……
面試官:好吧,那回去等通知吧
-
為什么會有結(jié)構(gòu)體內(nèi)存對齊?
-
結(jié)構(gòu)體怎么對齊?
-
學習結(jié)構(gòu)體對齊有什么用?
-
結(jié)構(gòu)體對齊有沒有實際應(yīng)用?
void base_type_size(void){BASE_TYPE_SIZE(void);BASE_TYPE_SIZE(char);BASE_TYPE_SIZE(short);BASE_TYPE_SIZE(int);BASE_TYPE_SIZE(long);BASE_TYPE_SIZE(long long);BASE_TYPE_SIZE(float);BASE_TYPE_SIZE(double);BASE_TYPE_SIZE(long double);BASE_TYPE_SIZE(void*);BASE_TYPE_SIZE(char*);BASE_TYPE_SIZE(int*);typedef struct{}StructNull;BASE_TYPE_SIZE(StructNull);BASE_TYPE_SIZE(StructNull*);}
void : 1 Bytechar : 1 Byteshort : 2 Bytesint : 4 Byteslong : 4 Byteslong long : 8 Bytesfloat : 4 Bytesdouble : 8 Byteslong double : 12 Bytesvoid* : 4 Byteschar* : 4 Bytesint* : 4 BytesStructNull : 0 ByteStructNull* : 4 Bytes
void類型不是空的,占一個字節(jié)
long不一定比int大
C語言空結(jié)構(gòu)體的大小為0(注意:C++的為1)
不管什么類型,指針都是相同大小的
typedef struct{int e_int;char e_char;}S1;S1 s1;STRUCT_E_ADDR_OFFSET(s1, e_int);STRUCT_E_ADDR_OFFSET(s1, e_char);typedef struct{int e_int;double e_double;}S11;S11 s11;STRUCT_E_ADDR_OFFSET(s11, e_int);STRUCT_E_ADDR_OFFSET(s11, e_double);
s1 size = 8 s1.e_int addr: 0028FF28, offset: 0s1 size = 8 s1.e_char addr: 0028FF2C, offset: 4s11 size = 16 s11.e_int addr: 0028FF18, offset: 0s11 size = 16 s11.e_double addr: 0028FF20, offset: 8
typedef struct{int e_int;long double e_ld;}S12;typedef struct{long long e_ll;long double e_ld;}S13;typedef struct{char e_char;long double e_ld;}S14;S12 s12;S13 s13;S14 s14;STRUCT_E_ADDR_OFFSET(s12, e_int);STRUCT_E_ADDR_OFFSET(s12, e_ld);STRUCT_E_ADDR_OFFSET(s13, e_ll);STRUCT_E_ADDR_OFFSET(s13, e_ld);STRUCT_E_ADDR_OFFSET(s14, e_char);STRUCT_E_ADDR_OFFSET(s14, e_ld);
s12 size = 16 s12.e_int addr: 0028FF08, offset: 0s12 size = 16 s12.e_ld addr: 0028FF0C, offset: 4s13 size = 24 s13.e_ll addr: 0028FEF0, offset: 0s13 size = 24 s13.e_ld addr: 0028FEF8, offset: 8s14 size = 16 s14.e_char addr: 0028FEE0, offset: 0s14 size = 16 s14.e_ld addr: 0028FEE4, offset: 4
每個特定平臺上的編譯器都有自己的默認“對齊系數(shù)”(也叫對齊模數(shù))。
網(wǎng)上流傳一個表:
平臺 |
長度/模數(shù) |
char |
short |
int |
long |
float |
double |
long long |
long double |
Win-32 |
長度 |
1 |
2 |
4 |
4 |
4 |
8 |
8 |
8 |
模數(shù) |
1 |
2 |
4 |
4 |
4 |
8 |
8 |
8 |
|
Linux-32 |
長度 |
1 |
2 |
4 |
4 |
4 |
8 |
8 |
12 |
模數(shù) |
1 |
2 |
4 |
4 |
4 |
4 |
4 |
4 |
|
Linux-64 |
長度 |
1 |
2 |
4 |
8 |
4 |
8 |
8 |
16 |
模數(shù) |
1 |
2 |
4 |
8 |
4 |
8 |
8 |
16 |
typedef struct{int e_int;double e_double;}S11;S11 s11;STRUCT_E_ADDR_OFFSET(s11, e_int);STRUCT_E_ADDR_OFFSET(s11, e_double);
s11 size = 16 s11.e_int addr: 0028FF18, offset: 0s11 size = 16 s11.e_double addr: 0028FF20, offset: 8
長度/模數(shù) |
char |
short |
int |
long |
float |
double |
long long |
long double |
長度 |
1 |
2 |
4 |
4 |
4 |
8 |
8 |
12 |
模數(shù) |
1 |
2 |
4 |
4 |
4 |
8 |
8 |
8 |
typedef struct{char e_char;long double e_ld;}S14;
typedef struct{int e_int;char e_char1;char e_char2;}S2;typedef struct{char e_char1;int e_int;char e_char2;}S3;S2 s2;S3 s3;
s2 size = 8 s2.e_int addr: 0028FED4, offset: 0s2 size = 8 s2.e_char1 addr: 0028FED8, offset: 4s2 size = 8 s2.e_char2 addr: 0028FED9, offset: 5s3 size = 12 s3.e_char1 addr: 0028FEC4, offset: 0s3 size = 12 s3.e_int addr: 0028FEC8, offset: 4s3 size = 12 s3.e_char2 addr: 0028FECC, offset: 8
typedef struct{char e_char1;short e_short;char e_char2;int e_int;char e_char3;}S4;S4 s4;STRUCT_E_ADDR_OFFSET(s4, e_char1);STRUCT_E_ADDR_OFFSET(s4, e_short);STRUCT_E_ADDR_OFFSET(s4, e_char2);STRUCT_E_ADDR_OFFSET(s4, e_int);STRUCT_E_ADDR_OFFSET(s4, e_char3);
s4 size = 16 s4.e_char1 addr: 0028FEB4, offset: 0s4 size = 16 s4.e_short addr: 0028FEB6, offset: 2s4 size = 16 s4.e_char2 addr: 0028FEB8, offset: 4s4 size = 16 s4.e_int addr: 0028FEBC, offset: 8s4 size = 16 s4.e_char3 addr: 0028FEC0, offset: 12
typedef struct{int e_int;char e_char;}S1;typedef struct{S1 e_s;char e_char;}SS1;typedef struct{short e_short;char e_char;}S6;typedef struct{S6 e_s;char e_char;}SS2;SS1 ss1;STRUCT_E_ADDR_OFFSET(ss1, e_s);STRUCT_E_ADDR_OFFSET(ss1, e_char);SS2 ss2;STRUCT_E_ADDR_OFFSET(ss2, e_s);STRUCT_E_ADDR_OFFSET(ss2, e_char);
ss1 size = 12 ss1.e_s addr: 0028FE94, offset: 0ss1 size = 12 ss1.e_char addr: 0028FE9C, offset: 8ss2 size = 6 ss2.e_s addr: 0028FE8E, offset: 0ss2 size = 6 ss2.e_char addr: 0028FE92, offset: 4
typedef union{char e_char;int e_int;}U1;U1 u1;STRUCT_E_ADDR(u1, e_char);STRUCT_E_ADDR(u1, e_int);
u1 size = 4 u1.e_char addr: 0028FF2Cu1 size = 4 u1.e_int addr: 0028FF2C
那么,union跟struct結(jié)合呢?
typedef struct{int e_int1;union{char ue_chars[9];int ue_int;}u;double e_double;int e_int2;}SU2;SU2 su2;STRUCT_E_ADDR_OFFSET(su2, e_int1);STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);STRUCT_E_ADDR_OFFSET(su2, u.ue_int);STRUCT_E_ADDR_OFFSET(su2, e_double);STRUCT_E_ADDR_OFFSET(su2, e_int2)
輸出:
su2 size = 32 su2.e_int1 addr: 0028FEF8, offset: 0su2 size = 32 su2.u.ue_chars addr: 0028FEFC, offset: 4su2 size = 32 su2.u.ue_int addr: 0028FEFC, offset: 4su2 size = 32 su2.e_double addr: 0028FF08, offset: 16su2 size = 32 su2.e_int2 addr: 0028FF10, offset: 24
實際上跟結(jié)構(gòu)體類似,也沒有特別的規(guī)則。
順便提一下,使用union時,要留意平臺的大小端問題。
大端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中,這樣的存儲模式有點兒類似于把數(shù)據(jù)當作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放;這和我們的閱讀習慣一致。
小端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中,這種存儲模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來,高地址部分權(quán)值高,低地址部分權(quán)值低。
百度百科——大小端模式
怎么獲知自己使用的平臺的大小端?Linux有個方法:
static union {char c[4];unsigned long l;} endian_test = { { 'l', '?', '?', 'b' } };printf("ENDIANNESS: %c\n", ENDIANNESS);
4. 位域(Bitfield)的相關(guān)
位域在本文沒什么好探討的,在結(jié)構(gòu)體對齊方面沒什么特別的地方。
直接看個測試代碼,就可以明白:
void bitfield_type_size(void){typedef struct{char bf1:1;char bf2:1;char bf3:1;char bf4:3;}SB1;typedef struct{char bf1:1;char bf2:1;char bf3:1;char bf4:7;}SB2;typedef struct{char bf1:1;char bf2:1;char bf3:1;int bfint:1;}SB3;typedef struct{char bf1:1;char bf2:1;int bfint:1;char bf3:1;}SB4;SB1 sb1;SB2 sb2;SB3 sb3;SB4 sb4;VAR_ADDR(sb1);VAR_ADDR(sb2);VAR_ADDR(sb3);VAR_ADDR(sb4);typedef struct{unsigned char bf1:1;unsigned char bf2:1;unsigned char bf3:1;unsigned char bf4:3;}SB11;typedef union{SB11 sb1;unsigned char e_char;}UB1;UB1 ub1;STRUCT_E_ADDR_OFFSET(ub1, sb1);STRUCT_E_ADDR_OFFSET(ub1, e_char);ub1.e_char = 0xF5;BITFIELD_VAL(ub1, e_char);BITFIELD_VAL(ub1, sb1.bf1);BITFIELD_VAL(ub1, sb1.bf2);BITFIELD_VAL(ub1, sb1.bf3);BITFIELD_VAL(ub1, sb1.bf4);}
輸出結(jié)果是:
sb1 size = 1 sb1 addr: 0028FF2Fsb2 size = 2 sb2 addr: 0028FF2Dsb3 size = 8 sb3 addr: 0028FF24sb4 size = 12 sb4 addr: 0028FF18ub1 size = 1 ub1.sb1 addr: 0028FF17, offset: 0ub1 size = 1 ub1.e_char addr: 0028FF17, offset: 0ub1 : 1 Byte, ub1.e_char=0xF5ub1 : 1 Byte, ub1.sb1.bf1=0x1ub1 : 1 Byte, ub1.sb1.bf2=0x0ub1 : 1 Byte, ub1.sb1.bf3=0x1ub1 : 1 Byte, ub1.sb1.bf4=0x6
有幾個點需要注意下:
內(nèi)存的計算單位是byte,不是bit
結(jié)構(gòu)體內(nèi)即使有bitfield元素,其對齊規(guī)則還是按照基本類型來
bitfield元素不能獲得其地址(即程序中不能通過&取址)
-
結(jié)構(gòu)體的內(nèi)存大小,并非其內(nèi)部元素大小之和;
-
結(jié)構(gòu)體變量的起始地址,可以被最大元素基本類型大小或者模數(shù)整除; -
結(jié)構(gòu)體的內(nèi)存對齊,按照其內(nèi)部最大元素基本類型或者模數(shù)大小對齊; -
模數(shù)在不同平臺值不一樣,也可通過#pragma pack(n)方式去改變; -
如果空間地址允許,結(jié)構(gòu)體內(nèi)部元素會拼湊一起放在同一個對齊空間; -
結(jié)構(gòu)體內(nèi)有結(jié)構(gòu)體變量元素,其結(jié)構(gòu)體并非展開后再對齊; -
union和bitfield變量也遵循結(jié)構(gòu)體內(nèi)存對齊原則。
typedef struct{int e_int;char e_char1;char e_char2;}S2;typedef struct{char e_char1;int e_int;char e_char2;}S3;S2 s2[1024] = {0};S3 s3[1024] = {0};
有時候,我們在通信數(shù)據(jù)接收處理時候,往往遇到,數(shù)組和結(jié)構(gòu)體的搭配。
即,通信時候,通常使用數(shù)組參數(shù)形式接收,而處理的時候,按照預(yù)定義格式去訪問處理。例如:
U8 comm_data[10];typedef struct{U8 id;U16 len;U8 data[6];}FRAME;FRAME* pFram = (FRAME*)comm_data;
// #define BASE_TYPE_SIZE(t) printf("%18s = %d\n", "sizeof("#t")", sizeof(t))void base_type_size(void){BASE_TYPE_SIZE(void);BASE_TYPE_SIZE(char);BASE_TYPE_SIZE(short);BASE_TYPE_SIZE(int);BASE_TYPE_SIZE(long);BASE_TYPE_SIZE(long long);BASE_TYPE_SIZE(float);BASE_TYPE_SIZE(double);BASE_TYPE_SIZE(long double);BASE_TYPE_SIZE(void*);BASE_TYPE_SIZE(char*);BASE_TYPE_SIZE(int*);}void struct_type_size(void){typedef struct{}StructNull;typedef struct{int e_int;char e_char;}S1;BASE_TYPE_SIZE(StructNull);BASE_TYPE_SIZE(StructNull*);S1 s1;STRUCT_E_ADDR_OFFSET(s1, e_int);STRUCT_E_ADDR_OFFSET(s1, e_char);typedef struct{int e_int;double e_double;}S11;typedef struct{int e_int;long double e_ld;}S12;typedef struct{long long e_ll;long double e_ld;}S13;typedef struct{char e_char;long double e_ld;}S14;S11 s11;S12 s12;S13 s13;S14 s14;STRUCT_E_ADDR_OFFSET(s11, e_int);STRUCT_E_ADDR_OFFSET(s11, e_double);STRUCT_E_ADDR_OFFSET(s12, e_int);STRUCT_E_ADDR_OFFSET(s12, e_ld);STRUCT_E_ADDR_OFFSET(s13, e_ll);STRUCT_E_ADDR_OFFSET(s13, e_ld);STRUCT_E_ADDR_OFFSET(s14, e_char);STRUCT_E_ADDR_OFFSET(s14, e_ld);typedef struct{int e_int;char e_char1;char e_char2;}S2;typedef struct{char e_char1;int e_int;char e_char2;}S3;typedef struct{char e_char1;short e_short;char e_char2;int e_int;char e_char3;}S4;typedef struct{long long e_ll;int e_int;}S5;typedef struct{S1 e_s;char e_char;}SS1;typedef struct{short e_short;char e_char;}S6;typedef struct{S6 e_s;char e_char;}SS2;char var1;S2 s2;char var2;S3 s3;VAR_ADDR(var1);STRUCT_E_ADDR_OFFSET(s2, e_int);STRUCT_E_ADDR_OFFSET(s2, e_char1);STRUCT_E_ADDR_OFFSET(s2, e_char2);VAR_ADDR(var2);STRUCT_E_ADDR_OFFSET(s3, e_char1);STRUCT_E_ADDR_OFFSET(s3, e_int);STRUCT_E_ADDR_OFFSET(s3, e_char2);S4 s4;STRUCT_E_ADDR_OFFSET(s4, e_char1);STRUCT_E_ADDR_OFFSET(s4, e_short);STRUCT_E_ADDR_OFFSET(s4, e_char2);STRUCT_E_ADDR_OFFSET(s4, e_int);STRUCT_E_ADDR_OFFSET(s4, e_char3);S5 s5;STRUCT_E_ADDR_OFFSET(s5, e_ll);STRUCT_E_ADDR_OFFSET(s5, e_int);SS1 ss1;STRUCT_E_ADDR_OFFSET(ss1, e_s);STRUCT_E_ADDR_OFFSET(ss1, e_char);SS2 ss2;STRUCT_E_ADDR_OFFSET(ss2, e_s);STRUCT_E_ADDR_OFFSET(ss2, e_char);}void union_type_size(void){typedef union{char e_char;int e_int;}U1;U1 u1;STRUCT_E_ADDR_OFFSET(u1, e_char);STRUCT_E_ADDR_OFFSET(u1, e_int);typedef struct{short e_short;union{char ue_chars[9];int ue_int;}u;}SU1;typedef struct{int e_int1;union{char ue_chars[9];int ue_int;}u;double e_double;int e_int2;}SU2;SU1 su1;SU2 su2;STRUCT_E_ADDR_OFFSET(su1, e_short);STRUCT_E_ADDR_OFFSET(su1, u.ue_chars);STRUCT_E_ADDR_OFFSET(su1, u.ue_int);STRUCT_E_ADDR_OFFSET(su2, e_int1);STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);STRUCT_E_ADDR_OFFSET(su2, u.ue_int);STRUCT_E_ADDR_OFFSET(su2, e_double);STRUCT_E_ADDR_OFFSET(su2, e_int2);}void bitfield_type_size(void){typedef struct{char bf1:1;char bf2:1;char bf3:1;char bf4:3;}SB1;typedef struct{char bf1:1;char bf2:1;char bf3:1;char bf4:7;}SB2;typedef struct{char bf1:1;char bf2:1;char bf3:1;int bfint:1;}SB3;typedef struct{char bf1:1;char bf2:1;int bfint:1;char bf3:1;}SB4;SB1 sb1;SB2 sb2;SB3 sb3;SB4 sb4;// STRUCT_E_OFFSET(sb1, bf1);// STRUCT_E_OFFSET(sb1, bf2);// STRUCT_E_OFFSET(sb1, bf3);// STRUCT_E_OFFSET(sb1, bf4);VAR_ADDR(sb1);VAR_ADDR(sb2);VAR_ADDR(sb3);VAR_ADDR(sb4);typedef struct{unsigned char bf1:1;unsigned char bf2:1;unsigned char bf3:1;unsigned char bf4:3;}SB11;typedef union{SB11 sb1;unsigned char e_char;}UB1;UB1 ub1;STRUCT_E_ADDR_OFFSET(ub1, sb1);STRUCT_E_ADDR_OFFSET(ub1, e_char);ub1.e_char = 0xF5;BITFIELD_VAL(ub1, e_char);BITFIELD_VAL(ub1, sb1.bf1);BITFIELD_VAL(ub1, sb1.bf2);BITFIELD_VAL(ub1, sb1.bf3);BITFIELD_VAL(ub1, sb1.bf4);static union {char c[4];unsigned long l;} endian_test = { { 'l', '?', '?', 'b' } };printf("ENDIANNESS: %c\n", ENDIANNESS);}int main(void){// base_type_size();struct_type_size();union_type_size();bitfield_type_size();return 0;}
-END-
本文授權(quán)轉(zhuǎn)載自公眾號“嵌入式軟件實戰(zhàn)派”,作者實戰(zhàn)派大師兄
推薦閱讀
免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!






