C语言结构体对齐问题,是面试必备问题。
+ L' r1 y, o% A2 G3 c4 E' v本文,除了用图解的方式讲清楚结构体知识点外,还将为你解答以下问题:- 为什么会有结构体内存对齐?
- 结构体怎么对齐?
- 学习结构体对齐有什么用?
- 结构体对齐有没有实际应用?
& R' U- h, I3 ~7 `
/ P. P5 h' o! ^. _
▍结构体内存对齐的原因 4 l1 j* i9 U# P% }' R6 b; I
一句话,为了提高效率,这个跟芯片设计有关。
# O& @" M; s7 V" y8 y' p* a& ~) i自从我们刚学习编程开始,就会接触到例如字、双字、四字等概念这里涉及到内存边界问题,它们的地址分别是可被2/4/8整除的。另外,在汇编中,不同长度的内存访问会用到不同的汇编指令。如果,一块内存在地址上随便放的,CPU有可能就会用到多条指令来访问,这就会降低效率。对于32位系统,如下图的A可能需要2条指令访问,而B只需1条指令。
4 I6 o! H0 g4 H% `8 s$ R) e- c
▍结构体内存对齐的规则 1. C语言基本类型的大小
- r Z, A; P0 z5 { \不要瞎猜,直接上代码。每个平台都不一样,请读者自行测试,以下我是基于Windows上MinGW的GCC测的。7 j/ ?4 O- m9 T m5 }
- 7 I+ I( W; c& V
- #define BASE_TYPE_SIZE(t) printf("%12s : %2d Byte%s\n", #t, sizeof(t), (sizeof(t))>1?"s":"")
6 ^ j3 G) F3 y" Y+ d8 [ - void base_type_size(void)
7 b6 F2 c$ ~9 j% P4 t, [+ l - {
# n2 J6 y+ C2 v$ \. S1 I - BASE_TYPE_SIZE(void);
5 i( [9 ?8 x$ @ - BASE_TYPE_SIZE(char);
& p. }$ {( O6 z9 E- z1 m, K" e - BASE_TYPE_SIZE(short);
3 ?- Q5 t; c @3 v) G) _1 a* V, o - BASE_TYPE_SIZE(int);
- n$ t. s' a0 `5 V - BASE_TYPE_SIZE(long);
7 ]' j/ K8 A3 R; T/ }$ A1 K1 g/ T - BASE_TYPE_SIZE(long long);
+ D6 i8 V+ x x9 y - BASE_TYPE_SIZE(float);
t9 J1 `3 d) K! x# e4 v - BASE_TYPE_SIZE(double);
. }0 }8 d% v# u3 o - BASE_TYPE_SIZE(long double);! D/ g/ e9 N) G, J
- BASE_TYPE_SIZE(void*);
1 D2 D3 p) G+ \8 E+ J Y - BASE_TYPE_SIZE(char*);- w" N" d! a2 M
- BASE_TYPE_SIZE(int*);
0 O J# Z; g2 o4 @1 Y0 e+ b/ A - : D' w& ]0 G) f% L3 U' P q$ ]
- typedef struct 9 K. {" B- i. s- Q' [# C
- {+ I/ R/ y9 d) w- t$ W1 n9 y
- }StructNull;9 K& r6 n" S+ z
- BASE_TYPE_SIZE(StructNull);
: z7 s. Q4 z2 d4 G - BASE_TYPE_SIZE(StructNull*);
; j d3 I: [8 P - }
复制代码 1 V4 e) m, x6 Z d0 }
结果是:
/ e# u) e: ?+ R, C( @. \) G# g9 Z8 b" m
- q0 W9 Y; { T' w; c+ V- void : 1 Byte
+ A" H8 A3 F* l' H G - char : 1 Byte
% o7 E: T) ^2 n' x - short : 2 Bytes0 G) o9 j" p, g- E
- int : 4 Bytes9 j8 A; ?( X: A2 [4 V8 R1 A: L
- long : 4 Bytes4 z* `7 X) R( @
- long long : 8 Bytes2 S8 Y7 n" a' x5 G" n B) W# a
- float : 4 Bytes" K% R. J( C1 m( t
- double : 8 Bytes c t- Q4 x7 @
- long double : 12 Bytes
$ f8 K2 D8 w! w0 `( W - void* : 4 Bytes5 i8 A' \% E |" j5 f" h
- char* : 4 Bytes* B+ k* c6 w& V1 Q
- int* : 4 Bytes6 [, {4 B: K8 P8 |4 J- {
- StructNull : 0 Byte
- A/ Z* p1 r D - StructNull* : 4 Bytes
复制代码
, d9 X U+ S2 O0 J! Y: @, U- k7 w6 L1 ~( y+ k* K- C
这些内容不用记住,不同平台是不一样的,使用之前,一定要亲自测试验证下,但是可以总结出以下信息:2. C语言结构体的内存对齐
+ B% Q h @) c* Q, z我先看个例子:( f/ m/ O# F$ q* [
- 4 x& J* Z+ b/ R0 N3 [- t* P/ }
- #define offset(type, member) (size_t)&(((type *)0)->member)& ?$ ?. ^0 |8 l4 v$ H% ?7 T
- #define STRUCT_E_ADDR(s,e) printf("%5s size = %2d %16s addr: %p\n", #s, sizeof(s), #s"."#e, &s.e)
9 x- d" k5 W \' @" V - #define STRUCT_E_OFFSET(s,e) printf("%5s size = %2d %16s offset: %2d\n", #s, sizeof(s), #s"."#e, offset(__typeof__(s),e))
% ?1 ]& o$ v9 O6 J - #define STRUCT_E_ADDR_OFFSET(s,e) printf("%5s size = %2d %16s addr: %p, offset: %2d\n", #s, sizeof(s), #s"."#e, &s.e, offset(__typeof__(s),e))
9 j' I/ r8 V0 q5 G: f9 _9 K
, ~" |3 B( r0 ]* {- typedef struct
$ y; D+ X' u2 z' ?( J% l - {
3 @' H0 f# C5 K - int e_int;
; [0 L, n2 m9 h. e: i - char e_char; W7 E/ X9 N1 q8 K0 A
- }S1;4 ?: p9 `5 b- ?
- S1 s1;
: z7 l7 ?+ @. y; ]. k: o - STRUCT_E_ADDR_OFFSET(s1, e_int);' n0 A: d; E: Y7 o0 k( |$ }" }
- STRUCT_E_ADDR_OFFSET(s1, e_char);, W- Y- t E7 S$ _* J# s
- typedef struct
0 [' l- c7 s% g6 ^2 w - {$ a6 k9 G8 m7 D1 A: g7 M+ p9 N4 e
- int e_int;
2 U( g6 @& V: _- \$ k8 v* T, h - double e_double;1 p: l* ?* C$ N) _0 Z
- }S11;* ?' C0 T7 N% T$ @* z& B" \
- S11 s11;
0 x* s- N* }, @1 m. N; { - STRUCT_E_ADDR_OFFSET(s11, e_int);
( Z6 ^+ O, r( z+ a0 A6 `; M - STRUCT_E_ADDR_OFFSET(s11, e_double);
复制代码 ; w) ?& ?0 X- Q$ M3 ^" w* f
. x2 ?0 }2 t: P; m6 e) D' }咦……这宏定义是啥意思?传送门:《基于C99规范,最全C语言预处理知识总结》输出结果:
( G% O5 b: C! ]- # p! j2 _* |0 t& y$ U: f. M
- s1 size = 8 s1.e_int addr: 0028FF28, offset: 0% o- i5 R+ y7 s) _* V6 E; p1 U6 L/ L
- s1 size = 8 s1.e_char addr: 0028FF2C, offset: 4& U8 C. n4 @9 O# A' ]! V
- s11 size = 16 s11.e_int addr: 0028FF18, offset: 0
1 R2 f5 `& D) _- ~ - s11 size = 16 s11.e_double addr: 0028FF20, offset: 8
复制代码 ( |. _4 F; j1 j. h( |
结论1:一般情况下,结构体所占的内存大小并非元素本身大小之和。& H0 o0 `9 [" k( L
% I9 i9 C, i) e4 N3 l结论2:不严谨地,结构体内存的大小按最大元素大小对齐。6 d8 h9 p) m7 ?* q* `2 q
继续看例子:
% i0 v! ?' y+ k9 ~! T- $ G7 B: Q2 p! {
- typedef struct 9 Q6 g, G2 q" U9 O9 }
- {! C* S" P& l7 @
- int e_int;
0 g! x3 v; ]+ [* a2 v- O - long double e_ld;6 ]( S5 g: Y6 {6 t+ [+ b- S
- }S12;9 w* G" ]7 U% f
- % u7 P n/ |+ E8 d5 h. v
- typedef struct 1 V' l+ A% S; a6 m; w$ J
- {
: a/ O% l ~* a1 Q& u - long long e_ll;
3 B1 U/ {7 o2 q+ I - long double e_ld;
) M; u) p# u; E: X2 W) E - }S13;2 U0 E4 k+ e8 b" `- V; W% `
- - Q6 u$ k2 l* [& q y
- typedef struct # W2 v0 n2 h2 J8 D# ~! S
- {5 V$ f4 }7 F. i% w" [% t
- char e_char;* m1 `7 b" v6 r0 k0 a$ \2 Y, G
- long double e_ld;
. l4 ^5 ?' i3 E: @ - }S14;
& ~7 v) f9 Y( U1 M3 V+ ~ - ) H5 A+ F4 D# k0 I4 R7 t% y
- S12 s12;3 F4 r0 r- h2 }) K: V3 b- }
- S13 s13;
4 b: _+ @* k% T - S14 s14;
0 \! w3 a; B8 u: O; F5 K% X3 P5 I - STRUCT_E_ADDR_OFFSET(s12, e_int);
+ }" W0 b3 f" a" z" ?* R - STRUCT_E_ADDR_OFFSET(s12, e_ld);- e+ q5 X9 b, k1 ~- ^
- STRUCT_E_ADDR_OFFSET(s13, e_ll);
+ `7 _' X% \( \2 l" B' \' [- F, R6 w - STRUCT_E_ADDR_OFFSET(s13, e_ld);
z6 ~: a! h9 W - STRUCT_E_ADDR_OFFSET(s14, e_char);' g2 a/ a- c# R1 G! P
- STRUCT_E_ADDR_OFFSET(s14, e_ld);
复制代码
& e7 ]' X6 Q8 h& U输出结果:$ K9 Q/ {' B' [3 t: R; G
7 s+ R I* C, _: d- s12 size = 16 s12.e_int addr: 0028FF08, offset: 0
9 f# G+ ~. {& w( I - s12 size = 16 s12.e_ld addr: 0028FF0C, offset: 4' q q, O C) c! n2 g9 z
- s13 size = 24 s13.e_ll addr: 0028FEF0, offset: 07 B& q/ Q6 x9 s# G, \5 S
- s13 size = 24 s13.e_ld addr: 0028FEF8, offset: 8
2 ]6 z! x* ^' G - s14 size = 16 s14.e_char addr: 0028FEE0, offset: 08 u( ~1 w) A9 V. Z1 y1 O7 R1 R
- s14 size = 16 s14.e_ld addr: 0028FEE4, offset: 4
复制代码 * F; j& C H8 {( x
, f& q" }: @& m) `( |& s
出现问题了,你看s12和s14,sizeof(long long)应该是12,按结论而推断sizeof(s12)和sizeof(s13)应该都是24。 ?9 t! P1 G# k9 ~
这里跟平台和编译器的一个模数有关。
/ ^0 I3 w: ]% F2 u7 M对结论2修正:结构体内存大小应按最大元素大小对齐,如果最大元素大小超过模数,应按模数大小对齐。. K _7 |$ n+ E
额外再送一条结论:如果结构体的最大元素大小超过模数,结构体的起始地址是可以被模数整除的。如果,最大元素大小没有超过模数大小,那它的起始地址是可以被最大元素大小整除。
! Q& q' w' q/ D6 ?- h, V1 ~那么,这个模数是什么?: ]! O8 S0 W) v5 I9 {+ L! [9 [" f
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。
网上流传一个表:& k& U6 V4 Q8 L3 K* @
平台 | 长度/模数 | char | short | int | long | float | double | long long | long double | Win-32 | 长度 | 1 | 2 | 4 | 4 | 4 | 8 | 8 | 8 | 模数 | 1 | 2 | 4 | 4 | 4 | 8 | 8 | 8 | Linux-32 | 长度 | 1 | 2 | 4 | 4 | 4 | 8 | 8 | 12 | 模数 | 1 | 2 | 4 | 4 | 4 | 4 | 4 | 4 | Linux-64 | 长度 | 1 | 2 | 4 | 8 | 4 | 8 | 8 | 16 | 模数 | 1 | 2 | 4 | 8 | 4 | 8 | 8 | 16 | 本文的的例子我用的是MinGW32的GCC来测试,你猜符合上表的哪一项?
1 q! P T) e' h. \别急,再看一个例子:
Z4 w1 e3 k! `& w4 o( X- H: F( y
4 _: ~# Z* O. |5 U+ G5 t2 \' h- typedef struct
, l2 v; ^8 P7 a% k+ O$ n* n - {
3 D. o; c6 t3 y - int e_int;
1 F& `8 ]6 a; y; J- h. M - double e_double;
% r' C$ g$ }7 R8 |4 i6 a0 j - }S11;8 q( z z- W8 j" O% m X% z. x
- S11 s11;; _: J$ U6 J# [: {
- STRUCT_E_ADDR_OFFSET(s11, e_int);
) \" l: m1 `5 N7 ` - STRUCT_E_ADDR_OFFSET(s11, e_double);
复制代码
/ } w# p F& Z2 h! @" t, ]6 B: _; z+ {" H8 g: [$ H
结果是:, [% j3 [! V0 w
2 D, S% Q* Q. l, G) J& V- s11 size = 16 s11.e_int addr: 0028FF18, offset: 0
- q& u! x) J& z S# | - s11 size = 16 s11.e_double addr: 0028FF20, offset: 8
复制代码
- o b1 u i& d9 u# t0 A! K) o' o" H' K2 f
很明显,上表没有一项完全对应得上的。简单汇总以下我测试的结果:, e$ T5 D+ D; G
长度/模数 | char | short | int | long | float | double | long long | long double | 长度 | 1 | 2 | 4 | 4 | 4 | 8 | 8 | 12 | 模数 | 1 | 2 | 4 | 4 | 4 | 8 | 8 | 8 | 所以,再强调一下:因为环境的差异,在你参考使用之前,请自行测试一下。另外,提一下:这个模数是可以改变的,可以用预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。例如( @. \ j L; r( @
" \) I4 Z5 U" Y- #pragma pack(1)
) W# @& F6 j) U3 E, m0 P5 c - typedef struct 2 }/ W j/ M; j. R0 a7 Y
- {3 s! S7 D( H. Y4 h( a2 {
- char e_char;7 j8 C" F. u. x z( R8 v9 P
- long double e_ld;& y$ M0 V' `0 [4 d6 I* K
- }S14;# C3 U7 P% ?' ]# ^( X6 P
- #pragma pack()
复制代码 ) F8 C; ~$ f* `2 Z
1 N1 H4 a0 `1 g! z5 r t#pragma是啥玩意?有兴趣可以看看:《基于C99规范,最全C语言预处理知识总结》, h. t ^2 n# O6 @5 R/ k! n) Q/ T6 h
好了,我们继续,这似乎没啥技术含量,我们提升下难度:
7 W) {1 l1 e( m4 X$ J. u* h
' t; d0 V* l4 K0 m1 O- typedef struct
0 w. W# |! P Z9 B4 _ - {0 {1 ~3 W8 C8 F* Q
- int e_int;
* |% I3 U9 S$ ~7 B - char e_char1;
& o' U$ b0 n3 | - char e_char2;; S( l. ^1 m* A2 r. g
- }S2;
, C: F* K0 e5 e# J3 I) u) \
`0 g/ G# w$ t0 I- typedef struct + Q) w, X! s( P) b% K
- {, T: E2 Q) y9 X
- char e_char1;
5 V. _9 \& h# w; b0 f$ r - int e_int;3 P* u) i3 W. m/ D
- char e_char2;3 b7 x8 y" o; O5 I
- }S3;1 Y( r8 d1 j# D1 D7 g
- S2 s2;
* F& f% ?9 j; c6 E' _8 v( g - S3 s3;
复制代码
8 Y5 Z% {( v/ c; r, Y+ m
5 X; W% ]+ x4 {* e2 C6 V, n: {你觉得这俩结构体所占内存是一样大吗?那你就错了:: M c F; g. p6 K6 j% C3 K4 }
- # j4 Y" M) Q: s# U: h
- s2 size = 8 s2.e_int addr: 0028FED4, offset: 0
6 M3 W% ^3 h! Z7 C/ y) l - s2 size = 8 s2.e_char1 addr: 0028FED8, offset: 4
2 I3 m5 @, Q( `8 i - s2 size = 8 s2.e_char2 addr: 0028FED9, offset: 52 A; i) {2 W, p" o6 f; y1 C! U( d
- s3 size = 12 s3.e_char1 addr: 0028FEC4, offset: 06 b1 c4 }; _! `, k4 L' _
- s3 size = 12 s3.e_int addr: 0028FEC8, offset: 4
& d3 ^2 V: S; _) t8 s3 B/ o- r - s3 size = 12 s3.e_char2 addr: 0028FECC, offset: 8
复制代码 ! ^8 t2 y& h. _% E/ @) N% K
: J: w2 T- H! q
why?
7 E0 ^" i$ B$ D上个图先看看,它们内存是怎么对齐的:7 V# x: C- q O
我们套一遍那几条结论就可以知道:5 Y* f4 c! |% _2 G, j- v( _
理解按最大元素大小或模数对齐,就可以看到S2的内存分布;
1 }0 a+ n% b9 f0 a7 E+ W对于S3,e_int的位置地址,肯定是要按int的大小对齐的(地址可被int大小整除),这样才能提高访问效率。同时,这导致了很大的内存浪费。
0 B6 t3 x3 N( t6 R& I; M E以上例子,我们看到挨在一起的两个char会放在同一个对齐单元,如果挨在一起的short和char会不会放一起?
' X0 \6 R' L1 @( I) h- ( {+ v% |$ ]8 e9 m& E' U
- typedef struct
4 C% i" f* l1 ^7 t! n - {
. ]; a% q9 t5 l - char e_char1;5 L' T- _- A# X5 b; P! O8 S" L
- short e_short;
g3 }: @* y) I" G% Z - char e_char2;
. p) ^8 R4 T `% N# ^) N - int e_int;
9 H" W+ |( o$ F' b0 |. h - char e_char3;
: k8 w& t+ q5 `) M4 i - }S4;; c3 O# x- _1 Y- ~4 n0 A0 i6 R) h4 h
- S4 s4;3 h. ~ n, u8 I3 x( L
- STRUCT_E_ADDR_OFFSET(s4, e_char1);
t N& H% t! K: Z - STRUCT_E_ADDR_OFFSET(s4, e_short);
( U( R$ t% J: y6 E) c! I# I - STRUCT_E_ADDR_OFFSET(s4, e_char2);5 w" A% X- E) p9 m8 |
- STRUCT_E_ADDR_OFFSET(s4, e_int);
# H( A! b3 ]- d( {! r7 {, g - STRUCT_E_ADDR_OFFSET(s4, e_char3);
复制代码 % q; E {# C4 I
) k& W" A9 X% b% A2 ^4 c
输出结果:& Z1 ~0 T! v. W' y
- ; T4 {. W7 |5 R3 e
- s4 size = 16 s4.e_char1 addr: 0028FEB4, offset: 0
! ` g$ T6 `$ N1 m9 o$ {& m) v - s4 size = 16 s4.e_short addr: 0028FEB6, offset: 2
1 _2 |/ p( \6 ^ - s4 size = 16 s4.e_char2 addr: 0028FEB8, offset: 4# \- e' N. l* m% P- Q8 ?9 N+ ?
- s4 size = 16 s4.e_int addr: 0028FEBC, offset: 8
' V' @% ~& W' U" R - s4 size = 16 s4.e_char3 addr: 0028FEC0, offset: 12
复制代码 ! P8 n, J8 i$ o
' E M* d0 K" W
得出一个经验:
# y. W1 S2 g% P) ~3 N% h8 e" g& r我们在定义结构体的时候,尽量把大小相同或相近的元素放一起,以减少结构体占用的内存空间。
8 E* Y, i0 I. y8 W再来一个问题:3 {2 t) i+ d. X3 z6 h
结构体套着另一个结构体怎么计算?8 Z3 }; q9 k2 ]. I! n/ _
0 Z# I0 k% I8 J6 x- `) [" I- typedef struct
z0 U: ~" v, F) m. Q/ e+ } - {+ p, d6 F5 |) m; L2 J: F, S
- int e_int;
, ~+ A5 h( W- C# K - char e_char;
1 G" q' q0 e& c - }S1;
/ r5 k* w& c/ [1 {3 C* F1 P) n( U0 b/ C - typedef struct 0 g$ b# u% }& J
- {
( F$ `6 M8 w: _$ S9 O - S1 e_s; @8 S; q# j$ U( t8 Q
- char e_char;6 t. d6 v! N2 a, P
- }SS1;9 a) W& p8 D. y7 A! z9 f1 E |
( l& v( ~' k* A( b) x+ m* g- typedef struct $ s8 O7 [8 g" J9 S3 Q$ C
- {
5 p" k2 R. Q7 n' F - short e_short;
( C Q. M2 o' N3 Y# r R2 U - char e_char;
; e- G, A9 g+ n - }S6;
4 m- | X1 b# E& y6 ]
3 r ?1 D8 @* {% e6 ?) Q; n l- typedef struct
* L7 o8 Z+ s% X2 e! Y9 n - {. K; y3 p) h, B% \2 y7 W" _+ g
- S6 e_s;
) R* l; l. V' u! F* M* M4 b: [ - char e_char;
; s8 S+ b2 W2 ]. j9 y - }SS2;
# H$ B6 q5 t. R0 C* ^ T -
1 A0 H5 S$ Q y, J - SS1 ss1;
7 `7 Z1 m3 H2 s' Z# q - STRUCT_E_ADDR_OFFSET(ss1, e_s);
0 P& ~3 O# D% X% ?. r' L1 x7 p6 ^ - STRUCT_E_ADDR_OFFSET(ss1, e_char);* r1 O. ~7 {+ V: v& q5 z: `/ q9 Y
- ; r0 b) k2 g6 E% Q' e3 j8 c
- SS2 ss2;) D/ y8 y0 V6 l1 V/ i
- STRUCT_E_ADDR_OFFSET(ss2, e_s);
, g) M+ N7 o/ Y( x0 y9 B; r; W - STRUCT_E_ADDR_OFFSET(ss2, e_char);
复制代码
# ~1 k; o T. V3 d2 f* K: I
0 m/ \* F1 S2 d0 L" y输出结果:$ M/ a7 d( E; z" n7 i' J; a+ T
- 3 h% F3 o0 `- n1 z" w
- ss1 size = 12 ss1.e_s addr: 0028FE94, offset: 0% s9 J, z& o% e" Q
- ss1 size = 12 ss1.e_char addr: 0028FE9C, offset: 8
& n, F$ Z) X- |% R/ m# F: k7 K - ss2 size = 6 ss2.e_s addr: 0028FE8E, offset: 0
) f) n2 [4 W2 Z; Q; O" ^ - ss2 size = 6 ss2.e_char addr: 0028FE92, offset: 4
复制代码 . W4 k0 c4 U2 {8 n* f$ g
' d/ H' F8 n2 O
得出结论:结构体内的结构体,结构体内的元素并不会和结构体外的元素合并占一个对齐单元。) N- W2 S! @) N+ k
温馨提示:大家不要刻意去记这些结论,动手去试试并思考下效果会更好。
4 p8 Y- ?" L6 R- y3. 联合体union的内存对齐" M4 W2 J4 a* Z3 c" Z, H
直接上代码:
# C- s0 j9 L- U* y2 v: Q* O
7 W) w1 _ m1 m5 P% i- typedef union
2 ~4 A! w' F& V/ `# ?( C$ G0 |/ I! b - {
& G- Z. n8 L, b* a7 p. h - char e_char;
! q9 i+ M$ X' [+ Q e - int e_int;/ w6 c1 U5 u0 z$ D1 ~! b1 h
- }U1;
8 I7 A0 q- z# A/ M2 F
5 y6 ~5 Q. l# p- U1 u1;- R( f' c/ I2 T1 t9 L Z+ w
- STRUCT_E_ADDR(u1, e_char);6 V' z* q2 l3 P+ @8 l2 b
- STRUCT_E_ADDR(u1, e_int);
复制代码
& M$ Z1 B7 {/ _3 a1 v
9 S9 n0 F' g v( M输出结果:
j, u3 _2 W& @; Z" M5 \4 [4 h
' i" y: B N0 z. P+ n- u1 size = 4 u1.e_char addr: 0028FF2C
: Y9 a0 c5 @" q! x - u1 size = 4 u1.e_int addr: 0028FF2C
复制代码
}9 C7 f/ L& B. x/ D& x( f) \. ?: E
从教科书上,我都可以理解,联合体里面的元素,实际上共享同一个空间。
7 d9 |! ?. c6 R# F+ O5 y那么,union跟struct结合呢? - - u; ?# n- g \" U% D
- typedef struct
' W A5 c+ G* c* m7 H - {8 h) k& S, q- ?
- int e_int1;
( E8 w1 ]1 j! F; \4 W - union
1 K7 w8 e! v; d. }0 }! Y$ o1 z - {
' o& {* n9 S! Q - char ue_chars[9]; 2 s1 l2 C9 f6 z9 M) M
- int ue_int;$ g# V1 S# b6 S7 Z4 ^, ?
- }u;
8 a4 K+ d3 J. w# v1 j! E - double e_double; , G( n+ o/ w. f! M
- int e_int2;
3 h0 p" D$ R @ - }SU2;+ p0 a( _: V4 i. b# _
- SU2 su2;
3 Y# t9 K4 _' `5 D - STRUCT_E_ADDR_OFFSET(su2, e_int1);/ p, I1 _$ M) q9 I% P. ?3 ?
- STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);7 ?2 b4 E1 Q' }0 U3 u
- STRUCT_E_ADDR_OFFSET(su2, u.ue_int);( e* X3 c1 u. s- I7 G, P% H4 J: ?7 x
- STRUCT_E_ADDR_OFFSET(su2, e_double);7 _) m% `( R0 R X
- STRUCT_E_ADDR_OFFSET(su2, e_int2)
复制代码
) p# @* i" ~+ ~& f输出:
* P8 [4 A8 P0 r; _! ]0 I" B$ C- su2 size = 32 su2.e_int1 addr: 0028FEF8, offset: 0
1 i) P; w0 g9 s$ i9 q - su2 size = 32 su2.u.ue_chars addr: 0028FEFC, offset: 4
+ T0 C% @- M% D$ J* Z - su2 size = 32 su2.u.ue_int addr: 0028FEFC, offset: 4, L2 `1 ^$ u6 E$ U) q
- su2 size = 32 su2.e_double addr: 0028FF08, offset: 169 W/ W" @7 r& t' N6 c7 q
- su2 size = 32 su2.e_int2 addr: 0028FF10, offset: 24
复制代码 $ T7 |$ H4 k; M3 d
实际上跟结构体类似,也没有特别的规则。 顺便提一下,使用union时,要留意平台的大小端问题。 大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。 小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。 百度百科——大小端模式
怎么获知自己使用的平台的大小端?Linux有个方法: - 3 v/ O3 j1 X5 i f0 g4 y- S
- static union { ! {$ s# V- a: v$ }
- char c[4]; , _- T* K) L: _, I8 C* H
- unsigned long l; 9 r0 c1 y/ y& f9 A% v+ @7 }
- } endian_test = { { 'l', '?', '?', 'b' } };" Y ?: @% ?: l: K! W( H" h0 R
- #define ENDIANNESS ((char)endian_test.l)" O8 r' B; {5 V3 h" f# ]
- ! h) C7 @/ a2 V% @0 H; N
- printf("ENDIANNESS: %c\n", ENDIANNESS);
复制代码
0 {) a1 R" v( X6 H* X
! X( |) D* ~. X$ y2 J& `. P
4. 位域(Bitfield)的相关 位域在本文没什么好探讨的,在结构体对齐方面没什么特别的地方。
* W9 v3 n, H! ]; j1 M: f' s! n e8 N 直接看个测试代码,就可以明白: - 7 x' t6 V5 g% o! o
- void bitfield_type_size(void)
8 E% ^# m ^& E3 z: b - {9 }. k) r* s# W6 \- F" ^4 e1 e
- typedef struct
% M6 E$ E! @8 v) i" r$ O - {
! m% G( Q+ P- [$ e - char bf1:1;
5 V3 [* G: N" x7 `% ?5 ~7 d - char bf2:1;, N5 A$ ` w/ E: ~
- char bf3:1;
5 R* K0 I7 Y Z. {# j1 Y - char bf4:3;" r. B! l! P3 w4 ]# ]8 t, X
- }SB1;3 n& Q4 i" S) O$ y5 ?8 Y3 M8 i; ?: n. W
' m7 N$ _% a# U: @- typedef struct
) L/ n! Q3 |$ d; v - {# D" x) I7 w8 A0 w* z; C9 T3 {1 N4 S% q0 P
- char bf1:1;9 Q0 D1 ?6 z3 F# u- R; B: ^7 [
- char bf2:1;
: X3 [2 U( d! ~+ h/ \1 S; S - char bf3:1;
M& P, s' S- F$ S - char bf4:7;2 ]. e9 v) K; ^ [* ~' C
- }SB2;
3 x7 j# v' i% \/ C) \ T8 F - ( U8 i6 \, o" p3 X
- typedef struct" m# e0 B0 ?; [0 ]& h2 j# [( k
- {( X0 D8 o+ X; i1 `1 o. f
- char bf1:1;
/ ] c" A4 x( Z& ] - char bf2:1;8 R; F) a B! Z) P9 G
- char bf3:1; l2 k$ s0 x/ B( ~7 I; L+ `2 y
- int bfint:1;. q0 v B0 y9 O( a8 ?
- }SB3;$ J$ `: a( b6 h1 g
- ; m( s% o1 m: W" O. G/ W" {( S
- typedef struct/ A! A( B- u0 u1 p7 }
- {, z) P7 R3 ~5 V. v) I
- char bf1:1;
$ U! E* H; _% v& v2 Y - char bf2:1;& f/ |+ g ^2 V+ o4 j
- int bfint:1;
7 g* k$ N3 c+ R# w - char bf3:1;
0 u" z) P; N# O - }SB4;
: u6 x) {3 P( J) K: k5 e, j" a
. }1 R/ j' F9 Q% Y6 s" R- SB1 sb1;
' _. @! a: m# m3 C3 I7 Q; F# ] - SB2 sb2;
8 b [# V, a/ [5 ?6 S - SB3 sb3;
- Q8 I" `1 z6 [ k; \$ o - SB4 sb4;, \* T$ K e1 K0 t I2 S" a$ J( O
- VAR_ADDR(sb1);
; r2 N# F \9 N4 W7 k2 ? - VAR_ADDR(sb2);, W* n$ \$ O; F p2 f/ F1 T
- VAR_ADDR(sb3);& ]' A3 m) ^5 s) N) }5 ^% k J
- VAR_ADDR(sb4);
& D% t' C. c T# W4 x5 I; B - # ~& A5 x* Y7 |8 Z: s
- typedef struct6 J8 Z& o6 ], W
- {- }8 I; f [* D- u( a: ^
- unsigned char bf1:1; d) y' l# U( l0 R) @1 M8 ^
- unsigned char bf2:1;0 t* \9 \- y1 h0 E2 [) a2 X7 m
- unsigned char bf3:1;
0 y- V% h8 U( a' E - unsigned char bf4:3;% J5 ^$ R% a1 I: G
- }SB11;) y0 r5 F7 h9 d; r
( F, y+ w: s* i; ?2 N) D0 B- typedef union 2 k4 [: E4 r' n1 e( v, D Z
- {3 @, V$ s' x; ~, V2 X0 r, `& N
- SB11 sb1;' V9 \" D9 Q, |% p: S, G8 E% x( c
- unsigned char e_char;$ E4 W4 t5 s& \# Q, {8 W
- }UB1;
# I, S8 b/ |8 v9 u, S - UB1 ub1;
3 L* O( x5 a% ^: [+ s" f" P9 ~$ z8 \ - 0 m8 I" l2 i z$ ~: } s0 j9 ~
- STRUCT_E_ADDR_OFFSET(ub1, sb1);! B! h7 j" L" z m u
- STRUCT_E_ADDR_OFFSET(ub1, e_char);6 @3 a+ C9 m# ~, f% ]8 ?8 N$ x
- # D! `8 @! _. j: v( n3 }/ O7 \
- ub1.e_char = 0xF5;
" x/ Y) c, _3 r0 K1 ` - BITFIELD_VAL(ub1, e_char);
$ o8 B+ Q3 A/ R( Y! V% l - BITFIELD_VAL(ub1, sb1.bf1);8 C, z# M, ^( [. D, l5 t. x7 W& p/ ]
- BITFIELD_VAL(ub1, sb1.bf2);
! i5 B5 U9 h; g; w - BITFIELD_VAL(ub1, sb1.bf3);2 Y3 [% s% `. S& X. K
- BITFIELD_VAL(ub1, sb1.bf4);
3 c m. O, t% }" p3 K% S3 X - }
复制代码
& I5 y4 `8 q; B( C$ L4 v输出结果是:
% j, I' j* j I# }$ u- sb1 size = 1 sb1 addr: 0028FF2F
' }* q1 Y9 h% _/ w/ F! x) |; s - sb2 size = 2 sb2 addr: 0028FF2D
9 Y- I3 U( ~: ?- e - sb3 size = 8 sb3 addr: 0028FF24& D. F8 Y( y+ U* O. C
- sb4 size = 12 sb4 addr: 0028FF18
1 O5 {: C$ c8 @9 B5 [" H - ub1 size = 1 ub1.sb1 addr: 0028FF17, offset: 06 m, h' l" z6 i3 F( H8 F$ D
- ub1 size = 1 ub1.e_char addr: 0028FF17, offset: 0' g% g+ K$ z' k) F5 {
- ub1 : 1 Byte, ub1.e_char=0xF5
+ u; b1 {; m* H9 }! A' e1 p - ub1 : 1 Byte, ub1.sb1.bf1=0x1
: p9 a1 k& W* y - ub1 : 1 Byte, ub1.sb1.bf2=0x0
( c6 G! O: r9 f7 C8 { - ub1 : 1 Byte, ub1.sb1.bf3=0x1* J5 B/ N( L9 y* w" _- |8 S; _
- ub1 : 1 Byte, ub1.sb1.bf4=0x6
复制代码
! h3 N& R' @# a$ b s% W- \有几个点需要注意下:& j- R9 ^, a6 \! `
内存的计算单位是byte,不是bit4 p U" D4 d c& Z
结构体内即使有bitfield元素,其对齐规则还是按照基本类型来 bitfield元素不能获得其地址(即程序中不能通过&取址)
# t/ u. C1 D! S
2 D/ S/ f! ~& H) H. `! H 5. 规则总结首先,不推荐记忆这些条条框框的文字,以下内容仅供参考:
6 p8 G3 e! H4 F3 G- 结构体的内存大小,并非其内部元素大小之和;
4 P6 Y- R' V4 e2 B p, s, O) Q - 结构体变量的起始地址,可以被最大元素基本类型大小或者模数整除;
- 结构体的内存对齐,按照其内部最大元素基本类型或者模数大小对齐;
- 模数在不同平台值不一样,也可通过#pragma pack(n)方式去改变;
- 如果空间地址允许,结构体内部元素会拼凑一起放在同一个对齐空间;
- 结构体内有结构体变量元素,其结构体并非展开后再对齐;
- union和bitfield变量也遵循结构体内存对齐原则。
" X# Z7 g* ~2 K; |( l. v5 w1 O
- V: l4 D( a8 ^
▍编程为什么要关注结构体内存对齐也许你会问,结构体爱怎么对齐就怎么对齐,我管它干嘛!( o* B: D: s% @8 {8 V! O6 |
1. 节省内存在嵌入式软件开发中,特别是内存资源匮乏的小MCU,这个尤为重要。如果优化程序内存,使得MCU可以选更小的型号,对于大批量出货的产品,可以带来更高利润。也许你还还感觉不到,上段代码:, S0 E" j2 L' @+ A5 _& b# n
- " [( T4 S4 d* _) ?" ^" Q
- typedef struct
0 P2 e$ G! U7 v9 ]9 N8 W - {9 \9 t$ L: u7 W8 ~& z7 n: A$ x
- int e_int;: _8 f2 K6 L! M4 G
- char e_char1;9 w4 |- n3 `1 B6 V$ r
- char e_char2;8 F9 b$ M/ Y; O! T1 K' X5 r
- }S2;
% k* `9 b5 S( @+ L - % _0 Z3 e5 o6 s5 o6 Q* c/ i- L5 b Z
- typedef struct
2 X D" G* s5 i: s4 @% N$ H; ~ - {! V( ^. a9 n* V% e1 E0 Q
- char e_char1;
$ {6 J* T# \6 Q+ J+ c7 A1 @$ Q - int e_int;& E T5 J% v8 x, b. ?4 Q+ i$ g
- char e_char2;
9 C* \0 C3 x1 y6 `3 Z - }S3;
$ r* R/ ] h0 n2 ^6 h -
; K% m' Z0 F" r - S2 s2[1024] = {0};0 X G: Y4 i; c+ I
- S3 s3[1024] = {0};
复制代码
) ^3 X$ ~) f- G4 k/ E2 U6 H
( S/ M9 z; V7 n, N7 X% k0 x( e8 Is2的大小为8K,而s3的大小为12K,一放大,就有很明显的区别了。* B9 R0 e2 n3 Q. ~" o1 O
2. union的内存对齐需要对于同一个内存,有时为了满足不同的访问形式,定义一个联合体变量,或者一个结构体和联合体组合的变量。此时就要知道其内存结构是怎么分布的。
/ }) s+ Y* b( {/ {+ c3 O' R/ A3. 内存拷贝有时候,我们在通信数据接收处理时候,往往遇到,数组和结构体的搭配。+ @4 d/ m+ c% _" `1 u. x
即,通信时候,通常使用数组参数形式接收,而处理的时候,按照预定义格式去访问处理。例如: - + L, S6 g% i- j
- U8 comm_data[10];
% Y. r- O! J# ?" _; X - typedef struct- ^' v {+ v" w' B$ {
- {
) M* D$ X. Z& x5 D2 {" o- S - U8 id;. u) S8 ~( f* |: U! d6 d0 C1 j
- U16 len;; s9 [" z, v) r G' g2 z' Y8 C- e3 @2 b
- U8 data[6];
4 z1 F5 S) O$ i' X% o - }FRAME;9 q/ B7 |: F3 P/ [
/ @* Y1 T. @4 [; U" B5 q- FRAME* pFram = (FRAME*)comm_data;
复制代码 ' p1 V) N9 M/ }" [
此处,必须要理解这个FRAM的内存结构是怎么样的对齐规则。" L7 G0 N0 d8 i3 f7 k; J1 @
4. 调试仿真时看压栈数据在调试某些奇葩问题时,迫不得已,我们会研究函数跳转或者线程切换时的栈数据,遇到结构体内容,肯定要懂得其内存对齐方式才能更好地获得栈内信息。6 C! J3 E4 R. E+ p6 e$ y7 L
当然,还有其他方面的原因,在此就不一一列举了。* r, J3 G2 h; I1 v/ W9 g
▍结构体内存对齐实际应用
+ `" `: Q' J' u' C) A, ]6 X- i上面一个章节已经部分讲到这个结构体内存对齐的应用了,例如通信数据的处理等。另外,再举两个例子:1. 内存的mapping假设你要做一个烧录文件,你想往文件头空间128个字节内放一段项目信息(例如程序大小、CRC校验码、其他项目信息等)。第一反应,你会考虑用一个结构体,定义一段这样的数据,程序运行的时候也定义同样的结构体去读取这个内存。但是你需要知道结构体大小啊,这个结构体内存对齐的规则还是需要了解的。2. 单片机寄存器的mapping在写MCU驱动的时候,访问寄存器的方式有很多种,但是做到清晰明了,适配性好的,往往需要诸多考量。0 e* R6 Q0 p; K N' K2 y
直接通过整型指针指到特定地址去访问,是没有问题的,但是对于某一类型的寄存器,往往不是一个固定地址,其后面还有一堆子寄存器属性需要配置。每个地址都通过整型指针访问,那就很多很凌乱。我们可以通过定义一个特定的结构体,用其指针直接mapping到寄存器的base地址。但是遇到有些地址是空的怎么办?甚至有些寄存器是32位的,有些16位,甚至8位的,各种参差不齐都在里面。那就要考虑结构体内存对齐了,特别是结构体内有不同类型的元素。这里只探讨应用场景,具体实现还要根据实际情况来定义。
/ y6 j; x# ?* k! A; c
▍测试源码- + P2 r# X. }/ T' R6 Z, [5 y
- #include <stdio.h>$ g8 \: b6 d+ K( R9 `# r
1 z* D4 w9 U% f: e. b9 w7 T- #define offset(type, member) (size_t)&(((type *)0)->member)
' E' l. Y5 \& k* h* {
_: w2 T" M/ M5 C+ Q& d- #define STRUCT_E_ADDR(s,e) printf("%5s size = %2d %16s addr: %p\n", #s, sizeof(s), #s"."#e, &s.e), I2 H; F, O/ b1 a z& j2 `
- #define STRUCT_E_OFFSET(s,e) printf("%5s size = %2d %16s offset: %2d\n", #s, sizeof(s), #s"."#e, offset(__typeof__(s),e))
# {/ r2 r! E; F - #define STRUCT_E_ADDR_OFFSET(s,e) printf("%5s size = %2d %16s addr: %p, offset: %2d\n", #s, sizeof(s), #s"."#e, &s.e, offset(__typeof__(s),e))' _! Q0 E7 s7 E& t
- #define VAR_ADDR(v) printf("%5s size = %2d %10s addr: %p\n", #v, sizeof(v), #v, &v)
# h" N4 x/ M: H X: \ - // #define BASE_TYPE_SIZE(t) printf("%18s = %d\n", "sizeof("#t")", sizeof(t))
, ?! ` C1 R" X: B - #define BASE_TYPE_SIZE(t) printf("%12s : %2d Byte%s\n", #t, sizeof(t), (sizeof(t))>1?"s":"")
! n3 v7 Q7 y! s. X - #define BITFIELD_VAL(s,e) printf("%12s : %2d Byte%s, %10s=0x%X\n", #s, sizeof(s), (sizeof(s))>1?"s":"", #s"."#e, s.e)9 l) a$ \- ]0 T% y m% ]) d6 v) F7 C
- 5 M0 |9 y! `5 h( s( X0 {1 }
- void base_type_size(void)
1 ^& `6 q s1 K9 v3 |/ o+ k# D% M - {
4 c" P0 p; U% [+ o2 i3 { - BASE_TYPE_SIZE(void);
& t% w3 J2 n- w1 V/ ~- \' i# ?6 T - BASE_TYPE_SIZE(char);( g. b1 c& J/ I4 [$ x0 U( v
- BASE_TYPE_SIZE(short);5 o n; x. l% H# H
- BASE_TYPE_SIZE(int);: |% P+ `8 _+ N m" w* F
- BASE_TYPE_SIZE(long);& _8 G3 y; a: m! G+ g( c- e' u9 A
- BASE_TYPE_SIZE(long long);
- V; f+ I6 N- X* Q0 j$ z* l - BASE_TYPE_SIZE(float);; t( B7 k- ^+ H! S9 X6 s
- BASE_TYPE_SIZE(double);: F0 J, Q1 N# v2 R c& |
- BASE_TYPE_SIZE(long double);
5 k$ [* C: O8 r, K0 O - BASE_TYPE_SIZE(void*);
- T0 V) O" ~$ J4 w1 R$ i - BASE_TYPE_SIZE(char*);
8 m; h! ^1 N7 \ - BASE_TYPE_SIZE(int*);
* D L% e$ a: ^ V$ n% o S7 {4 _3 I( z - }
: ^! O3 @" F3 {6 s4 |- S - void struct_type_size(void)7 j% K- B/ N" L0 C U! O& _ q1 h
- {9 o# t) l J* ]+ V
- typedef struct
$ ^. E4 z8 X& u. Z! z- S - {
8 D7 G1 @# ~5 A5 m( A - }StructNull;# w$ L: ]5 n, x+ }# n
+ A. t [$ ~5 \, `. }" g8 G- typedef struct
2 [: O3 H# G' A5 u - {
0 ]9 ~: M7 k1 r8 w - int e_int;# J" \, T$ R ^$ \1 G) B
- char e_char;
1 S4 w8 g: J. P( r9 p/ K - }S1;1 P4 k6 T& w0 x) x
- BASE_TYPE_SIZE(StructNull);
2 {& g: k4 u( Y2 N% ?4 c - BASE_TYPE_SIZE(StructNull*);' }1 |. B+ o4 c7 {$ D3 B! r
-
/ l" T; G. y3 a+ ^) U - S1 s1;
9 z; ~. j. U, b& F" a - STRUCT_E_ADDR_OFFSET(s1, e_int);2 }! s" a7 w( V, p5 V! }, _
- STRUCT_E_ADDR_OFFSET(s1, e_char);
5 ~! O a- T) E. |
$ w3 I8 n1 l# ?/ z- M1 q- typedef struct
$ f. O. F' N; \( _% B+ S - { d# f0 n( d) }( t( r2 I
- int e_int;
% R- l, V% ^+ d* Y" L - double e_double;
+ S+ ^3 d O2 u0 D) q7 q8 M - }S11;
4 T$ N- Z- ]( z) [3 s - $ u8 b4 s9 s: n- {9 w N0 @
- typedef struct 8 y# B# Q$ |8 O% q5 K
- {
# O+ L: I' P% K* Y1 Y5 V7 F - int e_int;+ C _# r! S8 q8 b2 B9 @& x) Z$ b
- long double e_ld;2 Q" v6 E+ `# @! `- j# o- D
- }S12;
% }; d6 J. N& \, }% K& S8 J
; k3 [. g, \1 T2 n1 t, K- typedef struct , {5 P5 L$ m2 I2 s; d
- {
; C0 U+ L; a9 y0 p2 \! q - long long e_ll;( O1 c2 j7 r; G1 `* f
- long double e_ld;: ~6 W+ z. K( S2 n
- }S13;
8 N; v' B: f6 [2 \ u* ` - * R( D( a# o: ?" c/ X( d9 I
- typedef struct
$ h9 x* C" J6 Q0 v8 i+ g: z0 ` - {: I O- a I! J. I6 _. Y9 p
- char e_char;
6 j! O: M3 p: K. V - long double e_ld;0 y: C2 L+ f- z3 u. e$ p# J) R2 U
- }S14;
5 H$ [; }+ Z4 Y3 r5 O0 {+ `
9 m' r% i8 ~& w+ X; S- g; F# p- S11 s11;
% s8 i$ N: z' D - S12 s12;- W0 l ^6 A7 Q3 U2 U
- S13 s13;
8 r/ M& W& z. H. R - S14 s14;
0 P( d- V; [) z- H% W3 R! ^1 W - STRUCT_E_ADDR_OFFSET(s11, e_int);" S" [ {* g3 f" J& I4 g
- STRUCT_E_ADDR_OFFSET(s11, e_double);& i1 t* l7 q1 M' F
- STRUCT_E_ADDR_OFFSET(s12, e_int); c" V8 R+ S5 P. f$ y
- STRUCT_E_ADDR_OFFSET(s12, e_ld);) x. W3 c' A5 N3 r! b8 _" z5 j; @
- STRUCT_E_ADDR_OFFSET(s13, e_ll);1 ?' {. Y' C+ l' z
- STRUCT_E_ADDR_OFFSET(s13, e_ld);! D/ x1 C8 ^& s& r; T2 w
- STRUCT_E_ADDR_OFFSET(s14, e_char);
$ H; o* ^1 W4 G - STRUCT_E_ADDR_OFFSET(s14, e_ld);4 Y" L- P% P4 O" `+ t
6 U$ [4 J1 m1 F( r# Y9 {' b! x- typedef struct
. }: |: M' n) Q* l7 f3 l0 Q1 ` - {
! W. H. ?4 `5 S8 w' k5 N& o. J9 B+ M - int e_int;
0 k) v8 l, a# P$ L2 n0 \. Q0 C' a - char e_char1;) c: c0 S% V" H, Y% b4 w
- char e_char2;
# x6 N0 r4 ]$ z - }S2;
6 G' I2 O9 j4 {- O1 l# v) K - 2 k5 H) G+ Q5 d) J7 R+ }( w, ^# p
- typedef struct
: x/ t3 X( Z: ?7 P i - {: Q& V0 m/ B4 Q
- char e_char1;
! f8 P# V4 l& s+ x" k - int e_int; [8 I4 Q3 g) E. T; _
- char e_char2;
/ t5 Q( Y! d( W- z1 }1 j3 R - }S3;! d. h) t+ R8 j s( o; R
- typedef struct
. i4 K4 N0 v8 v( B - {1 T, J1 {7 t, H7 l
- char e_char1;( s) L# l1 u. e6 {) [2 g8 O7 p* Z4 Z) [
- short e_short;
. N7 w$ [4 k9 n* Y6 N( p* l8 P - char e_char2;
% p N# x; L. U4 P - int e_int;% c) A5 w9 i$ ?
- char e_char3;% b* i4 i' o! F5 {$ D
- }S4;
P8 P6 a4 u7 {: z5 P6 k - typedef struct
. k" Q' n$ C4 T" L$ k5 f4 @0 Z' _: w$ @ - {
- b C6 Z2 a* ]% Z4 c% E. M1 Y - long long e_ll;0 }6 X) v! i6 i3 p8 h
- int e_int;' m8 x% s. \' S( Q! p$ c$ |0 Q! @. k
- }S5;$ U! ]2 d2 W( t6 R! n) w' O
0 N: Y" ]' Z! \* p- typedef struct ( i! \9 l- _( G+ q: D# h
- {4 q% [ P7 ~8 C6 w; s6 Q
- S1 e_s;
& T0 q5 U9 E8 ?4 F# @6 b7 Q - char e_char;" y) d1 F( A! N' | N
- }SS1;
J) Z3 t% d) q* _. U' `+ ~ h
$ u7 y+ Y, I) f1 p- a! E. R% ?- typedef struct ; F- p* o, W, t( B0 D I' s1 H
- {
7 B. n* N$ h9 W& q. \% {/ n - short e_short;
6 h! U% d( d" G, o4 g - char e_char;1 a5 r* j$ A' ^; a/ V
- }S6;- g" W/ q5 A, h; u: Y- {# S, A
+ ?6 d9 |8 M$ l `' w4 h* i l- f- typedef struct
8 ^) v) H; g$ \9 W- `5 c3 M - {6 L1 r2 ^$ |( }6 I) l* M @0 Z( f
- S6 e_s;
2 P. |- G: d. N$ C) r$ N& r1 P - char e_char;
+ ?, F m; [- O - }SS2;
% h, I/ t }7 n" j
! Y; s: M* _# N1 o6 ?) ^. M
$ x1 X5 E( M# y g; W0 \+ s8 z- char var1;
% [. S) D8 q% w7 h1 G" a% ^7 l - S2 s2;
4 Q8 d2 q) Q( E; c4 A/ U, N - char var2;4 L7 l$ u% F" k( w
- S3 s3;/ s) [- |* `' {' x$ O3 u1 W
- VAR_ADDR(var1);
# z- j# Z0 n0 K/ Z4 i+ k" ~$ x4 ] - STRUCT_E_ADDR_OFFSET(s2, e_int);2 N; D; V2 f8 m6 t& J
- STRUCT_E_ADDR_OFFSET(s2, e_char1);
9 D3 a% W& P4 f, N7 O3 V5 i - STRUCT_E_ADDR_OFFSET(s2, e_char2);5 m( e9 \+ T# t! a
- VAR_ADDR(var2);* l4 v) c d: `: T
- STRUCT_E_ADDR_OFFSET(s3, e_char1);& W! S* K: R2 D: p1 k, `( |* u: b
- STRUCT_E_ADDR_OFFSET(s3, e_int);* ]. N/ n+ L% v$ F
- STRUCT_E_ADDR_OFFSET(s3, e_char2);. g! ]2 Y1 Z* ]5 V7 e& H$ ~
- S4 s4;5 l' S% a5 Q* J0 y/ W V$ M4 R0 X
- STRUCT_E_ADDR_OFFSET(s4, e_char1);5 i+ m z$ l$ N2 T+ A& {- d0 T
- STRUCT_E_ADDR_OFFSET(s4, e_short);% V; z+ S5 f: a
- STRUCT_E_ADDR_OFFSET(s4, e_char2);
. v: d3 i, ~, v3 q5 y% i - STRUCT_E_ADDR_OFFSET(s4, e_int);8 b8 A0 |. K! T5 I6 Y v
- STRUCT_E_ADDR_OFFSET(s4, e_char3);
; _* l1 Y/ l7 r - S5 s5;0 u B5 d* @1 I6 W r+ j! ~- D
- STRUCT_E_ADDR_OFFSET(s5, e_ll);
7 ]3 D; I! U- v- K& I- y% v$ j - STRUCT_E_ADDR_OFFSET(s5, e_int);1 ^3 S* g; G& {% n6 ?3 G' M
- SS1 ss1;# _1 g; E# g0 o
- STRUCT_E_ADDR_OFFSET(ss1, e_s);
# {1 S0 E: z! o( \ - STRUCT_E_ADDR_OFFSET(ss1, e_char);
K- B1 B& u$ w( l( I$ v6 R# a4 ^
" w) s& |; b' {' U$ _- SS2 ss2;
* J' }$ }9 A$ `( h5 a - STRUCT_E_ADDR_OFFSET(ss2, e_s);) J8 J& P" h+ W) U+ R4 } W
- STRUCT_E_ADDR_OFFSET(ss2, e_char);
) M/ R7 W! ?+ w# `! U9 T, x - }! n k3 X! n3 W( {
- + u) p; Z0 z! `# a
- void union_type_size(void)
+ E7 v) ~5 e! b! m" U5 r - {
8 d$ b4 E/ t6 w9 }& }: G - typedef union $ ?: r2 n, K- X
- {
& A7 Z6 [% A6 \) Q1 P: g& [- y y - char e_char;0 E1 o; P& C) n4 ^! q
- int e_int;2 ]6 _( i* j: s1 ]8 P# H
- }U1;0 ^: U' T z+ H) Y+ V# P2 e
1 T2 k1 i% [, B' S- U1 u1;
+ _8 ^# x9 n4 k4 D - STRUCT_E_ADDR_OFFSET(u1, e_char);
( \4 O# f8 a1 S: e! J - STRUCT_E_ADDR_OFFSET(u1, e_int);4 s) k1 l! ?. Y! U# p: L
-
; A5 V5 x& M+ s1 s& | - 1 }% r/ d# X- w2 J6 o# T
- typedef struct6 F: {4 J. e0 }, c# ~; X8 O
- {
L3 f. j7 ]9 x# v. F+ ~ - short e_short;
$ t0 c6 Z b# |+ `- ^ - union6 g8 I# d8 B! S6 {6 ~
- {
- n2 M' n7 @6 n% X - char ue_chars[9];: z5 M: ^& N% r' O1 G
- int ue_int;& A# @2 D1 U2 O1 N6 Y+ N \
- }u;
4 H& J9 M, a5 a% j - }SU1;0 C2 \5 B& B+ y7 T0 B
$ g) \& v. v" J# q* l/ f- M! p- typedef struct
: z Y' \# k ~8 P- L, b# ~1 z) _ - {1 o9 f/ y+ U" J' Q
- int e_int1; 7 _! M( W) ^6 h2 X
- union
t. y% S, g/ q4 R* e8 W4 z - {
/ [6 E1 n; `$ F2 U2 q - char ue_chars[9];
( j% H' v( [0 m" A5 | - int ue_int;8 ~+ ]0 J" Q. Z8 n3 l- m# i( o
- }u;
4 f# `# h4 C: s B* j- @% E1 _7 o - double e_double;
/ L! ]; G+ I3 d6 p: ]4 J4 E - int e_int2;
% a7 E$ b3 j3 n" d - }SU2;
0 E- m+ Z4 x4 Q9 ?: N# a - 2 x% @ L9 r( y4 G( `" ~* j) ]* r- [
- SU1 su1;
0 q( X5 @# o+ a3 c. t' q4 ]4 d - SU2 su2;
6 N, k6 g5 ?+ [& x+ W2 D* e% } Q -
; Y% `& m7 R2 W8 K6 L1 I" G1 d# |- R - STRUCT_E_ADDR_OFFSET(su1, e_short);
6 m; ^( @1 H/ g; Y% z' g - STRUCT_E_ADDR_OFFSET(su1, u.ue_chars);
`* e S: T* S d3 r - STRUCT_E_ADDR_OFFSET(su1, u.ue_int);3 o$ i6 d% M5 ^" E0 b
-
+ |! U( t( q, m9 U( ? - STRUCT_E_ADDR_OFFSET(su2, e_int1);% z3 [6 k3 F+ ]' O5 L3 u
- STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);
. ~/ A4 r# R2 P - STRUCT_E_ADDR_OFFSET(su2, u.ue_int);/ q% j; E2 L, B+ w% x
- STRUCT_E_ADDR_OFFSET(su2, e_double);
8 Q, Y7 y# }6 ` p! }# ?, H - STRUCT_E_ADDR_OFFSET(su2, e_int2);
_: d3 B; i: T6 B! \4 ~6 X; K. h* z - }. N0 s; o" |( c$ o/ R/ H$ a
4 _5 n* h8 P( F' P/ h* F- void bitfield_type_size(void), G( P% z: }, S1 G1 |
- {# @" A! S- F, s$ J( h: m
- typedef struct4 \ n$ i( [. s( _
- {
7 \% S G+ N8 l: Y - char bf1:1;
8 ?: [% X3 E" i0 ?3 j' T: B& R( r0 b - char bf2:1;( h4 K8 f$ d" r: O8 j0 m/ r! ~
- char bf3:1;- f- r4 p) p/ H9 V
- char bf4:3;9 a8 s' N+ E5 }* R5 B; r- Z
- }SB1;
, ^! n1 |" X6 a - - G5 v7 |# {; J1 L; z$ ^
- typedef struct
" S ?9 T4 d, b2 G5 O) z4 [ - {
$ u# U4 I: {/ |2 } - char bf1:1;, O; x: E1 ^5 m- E6 ~, C
- char bf2:1;
# C9 k& X. A$ }2 l - char bf3:1;
F! P7 ?; v: y& x - char bf4:7;$ a$ X( E: @( Z& z. \7 M
- }SB2;. |) c% _# F' A" w( C% t0 S
- + [5 u" X7 k/ K4 R$ ?. \" ^) X
- typedef struct, q* I- G/ y8 H
- {" A; ]& G* _( e. I7 T3 p
- char bf1:1;
' {; `( d: _; q' ?( \ - char bf2:1;6 J, H: s5 F6 J3 O- V1 T
- char bf3:1;* }% z n ^; s' ^0 h
- int bfint:1;2 Z! h) J8 Y7 Q) [' K \; C& Q
- }SB3;
- y; B$ w4 ]* }+ ^4 y/ t
. B9 g8 m, l) K2 K- typedef struct( u' I/ W! i! @$ w* U
- {) H# E1 B6 m. W. O
- char bf1:1;
& ~/ F1 u! x3 Y+ I ~# y5 B/ Y [ - char bf2:1;. t. M: n/ _$ r5 Y( o! Z
- int bfint:1;0 O* \' {: {6 G$ i2 S2 Y" ^
- char bf3:1;
$ Q: J" {$ l% e, E* i# e - }SB4;& b2 Y7 f0 V1 B1 t5 x& k. P
- / e$ W( k' s" C7 V6 g. f
- SB1 sb1;! d6 [3 C! U7 R- F: {& |" O+ g! [; q
- SB2 sb2;
4 n! t; \. K$ M9 Q - SB3 sb3;+ g! Y0 N$ L! B
- SB4 sb4;1 {6 ~$ J }" j, w% l. R
- // STRUCT_E_OFFSET(sb1, bf1);
4 x3 S+ J! K9 f - // STRUCT_E_OFFSET(sb1, bf2);
+ b( V5 [3 J, ~1 n" R5 S - // STRUCT_E_OFFSET(sb1, bf3);
% i' h/ @& R+ ? |- l0 X1 m - // STRUCT_E_OFFSET(sb1, bf4);9 y! a" A$ x, o9 y
- VAR_ADDR(sb1);
4 [+ B: B* g7 w. r1 Z9 L. d - VAR_ADDR(sb2);
- G$ M( o! i: L - VAR_ADDR(sb3);
0 j6 `" t r9 p4 [5 }+ A - VAR_ADDR(sb4);4 X& t& {$ v) @# O/ J/ H
- * U; c) U v H: `; m
- typedef struct
8 ?& \5 B& E: G2 J) o - {
/ l9 \' @3 p6 W7 D2 {% M - unsigned char bf1:1;0 g' V6 }# i3 ~# o7 V; |% a
- unsigned char bf2:1;) w, [7 ~' a! l5 V1 M' m. P
- unsigned char bf3:1;
$ x7 y% H. H) [3 k. H! y C - unsigned char bf4:3;, x! ~3 J3 X, h5 e# A
- }SB11;- S$ a8 D2 l8 u" ]6 n( ?9 d, q0 w- a2 b
4 s# b; w( C2 I% O( ~ y- typedef union 6 D/ H, a8 c' Q1 A
- {2 }% @* u. j* L) u* t& b2 @
- SB11 sb1; q6 f' n# x$ M2 A( Z
- unsigned char e_char;
3 G2 |: {& }5 x) K/ S2 A& q - }UB1;
# d p4 R4 L( L2 N9 ^( `* S - UB1 ub1;" m6 w, Y. V p' V, P3 P
- ) y6 @& C3 d# Z: L! [& M- z
- STRUCT_E_ADDR_OFFSET(ub1, sb1);
( d2 Y& J0 s; \; q- x - STRUCT_E_ADDR_OFFSET(ub1, e_char);
3 z' I: M9 |" u - . m" s( \+ j8 a. R8 m9 k6 z
- ub1.e_char = 0xF5;
! m9 b+ V8 s4 a+ G, `$ Q - BITFIELD_VAL(ub1, e_char);/ _: G: `0 O& D6 n/ s! P: Y
- BITFIELD_VAL(ub1, sb1.bf1);0 i# V/ Z! I3 Y7 @9 @3 @
- BITFIELD_VAL(ub1, sb1.bf2);
6 C1 b: r2 ^& v6 R - BITFIELD_VAL(ub1, sb1.bf3);4 C$ D" }4 N! H9 g+ t# K6 m, C
- BITFIELD_VAL(ub1, sb1.bf4);2 D- m/ B9 @5 s# i: |" j" `
! K. Q( _: u9 g+ _$ d# H' i4 Z8 y- Q- static union {
( ^$ e1 S: u$ b5 I" i: [4 |/ s - char c[4]; ) h& O: s1 k# F8 m
- unsigned long l;
0 [ E# Y- d% `5 K1 T8 A2 o - } endian_test = { { 'l', '?', '?', 'b' } }; H2 ` Q- m0 m( U+ V1 k
- #define ENDIANNESS ((char)endian_test.l)# q! V& O) o9 ]$ u
- " ~9 D7 p1 I+ w9 L/ f* z
- printf("ENDIANNESS: %c\n", ENDIANNESS);* w" ~6 H( E3 u( H& T2 U$ ]+ g
) k, a8 {) I# T3 z) k& K- }1 K/ S- y* A2 I1 h
- int main(void)
, X, ~" E. v( i4 C; k: H - {) j0 _$ B" o A' ^7 G% r
- // base_type_size();
; X0 |! H7 h7 g D! e3 \ - struct_type_size();" }' P0 K+ _7 _! r7 }2 x
- union_type_size();
" T8 \, _, M1 G* m/ h2 C - bitfield_type_size();; R$ U: J; ~9 x% F
: J: p) X# J; \* z0 d( J/ l- return 0;, `3 g* Q5 n( p! h7 g$ h, q
- }
复制代码 + N" Q; B: G9 A' w
' j: I+ K$ f8 x2 q |