你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言结构体对齐问题

[复制链接]
gaosmile 发布时间:2020-6-17 13:01
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
微信图片_20200617125952.png
微信图片_20200617125955.png

▍结构体内存对齐的规则
1. C语言基本类型的大小
- r  Z, A; P0 z5 {  \
不要瞎猜,直接上代码。每个平台都不一样,请读者自行测试,以下我是基于Windows上MinGW的GCC测的。7 j/ ?4 O- m9 T  m5 }
  1. 7 I+ I( W; c& V
  2. #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 [
  3. void base_type_size(void)
    7 b6 F2 c$ ~9 j% P4 t, [+ l
  4. {
    # n2 J6 y+ C2 v$ \. S1 I
  5.     BASE_TYPE_SIZE(void);
    5 i( [9 ?8 x$ @
  6.     BASE_TYPE_SIZE(char);
    & p. }$ {( O6 z9 E- z1 m, K" e
  7.     BASE_TYPE_SIZE(short);
    3 ?- Q5 t; c  @3 v) G) _1 a* V, o
  8.     BASE_TYPE_SIZE(int);
    - n$ t. s' a0 `5 V
  9.     BASE_TYPE_SIZE(long);
    7 ]' j/ K8 A3 R; T/ }$ A1 K1 g/ T
  10.     BASE_TYPE_SIZE(long long);
    + D6 i8 V+ x  x9 y
  11.     BASE_TYPE_SIZE(float);
      t9 J1 `3 d) K! x# e4 v
  12.     BASE_TYPE_SIZE(double);
    . }0 }8 d% v# u3 o
  13.     BASE_TYPE_SIZE(long double);! D/ g/ e9 N) G, J
  14.     BASE_TYPE_SIZE(void*);
    1 D2 D3 p) G+ \8 E+ J  Y
  15.     BASE_TYPE_SIZE(char*);- w" N" d! a2 M
  16.     BASE_TYPE_SIZE(int*);
    0 O  J# Z; g2 o4 @1 Y0 e+ b/ A
  17.     : D' w& ]0 G) f% L3 U' P  q$ ]
  18.     typedef struct 9 K. {" B- i. s- Q' [# C
  19.     {+ I/ R/ y9 d) w- t$ W1 n9 y
  20.     }StructNull;9 K& r6 n" S+ z
  21.     BASE_TYPE_SIZE(StructNull);
    : z7 s. Q4 z2 d4 G
  22.     BASE_TYPE_SIZE(StructNull*);
    ; j  d3 I: [8 P
  23. }
复制代码
1 V4 e) m, x6 Z  d0 }
结果是:
/ e# u) e: ?+ R, C( @. \) G# g9 Z8 b" m

  1. - q0 W9 Y; {  T' w; c+ V
  2.         void :  1 Byte
    + A" H8 A3 F* l' H  G
  3.         char :  1 Byte
    % o7 E: T) ^2 n' x
  4.        short :  2 Bytes0 G) o9 j" p, g- E
  5.          int :  4 Bytes9 j8 A; ?( X: A2 [4 V8 R1 A: L
  6.         long :  4 Bytes4 z* `7 X) R( @
  7.    long long :  8 Bytes2 S8 Y7 n" a' x5 G" n  B) W# a
  8.        float :  4 Bytes" K% R. J( C1 m( t
  9.       double :  8 Bytes  c  t- Q4 x7 @
  10. long double : 12 Bytes
    $ f8 K2 D8 w! w0 `( W
  11.        void* :  4 Bytes5 i8 A' \% E  |" j5 f" h
  12.        char* :  4 Bytes* B+ k* c6 w& V1 Q
  13.         int* :  4 Bytes6 [, {4 B: K8 P8 |4 J- {
  14.   StructNull :  0 Byte
    - A/ Z* p1 r  D
  15. StructNull* :  4 Bytes
复制代码

, d9 X  U+ S2 O0 J! Y: @, U- k7 w6 L1 ~( y+ k* K- C
这些内容不用记住,不同平台是不一样的,使用之前,一定要亲自测试验证下,但是可以总结出以下信息:
  • void类型不是空的,占一个字节
    0 }5 ?( t/ C7 E" `3 L& |! W
  • long不一定比int大
  • C语言空结构体的大小为0(注意:C++的为1)
  • 不管什么类型,指针都是相同大小的
      H! M2 h" V1 u, ]8 {+ C0 t2 i
    8 a& O$ j$ u8 x! ]/ Z
2. C语言结构体的内存对齐
+ B% Q  h  @) c* Q, z我先看个例子:( f/ m/ O# F$ q* [
  1. 4 x& J* Z+ b/ R0 N3 [- t* P/ }
  2. #define offset(type, member)      (size_t)&(((type *)0)->member)& ?$ ?. ^0 |8 l4 v$ H% ?7 T
  3. #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
  4. #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
  5. #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

  6. , ~" |3 B( r0 ]* {
  7. typedef struct
    $ y; D+ X' u2 z' ?( J% l
  8. {
    3 @' H0 f# C5 K
  9.     int e_int;
    ; [0 L, n2 m9 h. e: i
  10.     char e_char;  W7 E/ X9 N1 q8 K0 A
  11. }S1;4 ?: p9 `5 b- ?
  12. S1 s1;
    : z7 l7 ?+ @. y; ]. k: o
  13. STRUCT_E_ADDR_OFFSET(s1, e_int);' n0 A: d; E: Y7 o0 k( |$ }" }
  14. STRUCT_E_ADDR_OFFSET(s1, e_char);, W- Y- t  E7 S$ _* J# s
  15. typedef struct
    0 [' l- c7 s% g6 ^2 w
  16. {$ a6 k9 G8 m7 D1 A: g7 M+ p9 N4 e
  17.     int e_int;
    2 U( g6 @& V: _- \$ k8 v* T, h
  18.     double e_double;1 p: l* ?* C$ N) _0 Z
  19. }S11;* ?' C0 T7 N% T$ @* z& B" \
  20. S11 s11;
    0 x* s- N* }, @1 m. N; {
  21. STRUCT_E_ADDR_OFFSET(s11, e_int);
    ( Z6 ^+ O, r( z+ a0 A6 `; M
  22. 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! ]
  1. # p! j2 _* |0 t& y$ U: f. M
  2.    s1 size =  8         s1.e_int addr: 0028FF28, offset:  0% o- i5 R+ y7 s) _* V6 E; p1 U6 L/ L
  3.    s1 size =  8        s1.e_char addr: 0028FF2C, offset:  4& U8 C. n4 @9 O# A' ]! V
  4.   s11 size = 16        s11.e_int addr: 0028FF18, offset:  0
    1 R2 f5 `& D) _- ~
  5.   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
  1. $ G7 B: Q2 p! {
  2.     typedef struct 9 Q6 g, G2 q" U9 O9 }
  3.     {! C* S" P& l7 @
  4.         int e_int;
    0 g! x3 v; ]+ [* a2 v- O
  5.         long double e_ld;6 ]( S5 g: Y6 {6 t+ [+ b- S
  6.     }S12;9 w* G" ]7 U% f
  7. % u7 P  n/ |+ E8 d5 h. v
  8.     typedef struct 1 V' l+ A% S; a6 m; w$ J
  9.     {
    : a/ O% l  ~* a1 Q& u
  10.         long long e_ll;
    3 B1 U/ {7 o2 q+ I
  11.         long double e_ld;
    ) M; u) p# u; E: X2 W) E
  12.     }S13;2 U0 E4 k+ e8 b" `- V; W% `
  13. - Q6 u$ k2 l* [& q  y
  14.     typedef struct # W2 v0 n2 h2 J8 D# ~! S
  15.     {5 V$ f4 }7 F. i% w" [% t
  16.         char e_char;* m1 `7 b" v6 r0 k0 a$ \2 Y, G
  17.         long double e_ld;
    . l4 ^5 ?' i3 E: @
  18.     }S14;
    & ~7 v) f9 Y( U1 M3 V+ ~
  19. ) H5 A+ F4 D# k0 I4 R7 t% y
  20.     S12 s12;3 F4 r0 r- h2 }) K: V3 b- }
  21.     S13 s13;
    4 b: _+ @* k% T
  22.     S14 s14;
    0 \! w3 a; B8 u: O; F5 K% X3 P5 I
  23.     STRUCT_E_ADDR_OFFSET(s12, e_int);
    + }" W0 b3 f" a" z" ?* R
  24.     STRUCT_E_ADDR_OFFSET(s12, e_ld);- e+ q5 X9 b, k1 ~- ^
  25.     STRUCT_E_ADDR_OFFSET(s13, e_ll);
    + `7 _' X% \( \2 l" B' \' [- F, R6 w
  26.     STRUCT_E_ADDR_OFFSET(s13, e_ld);
      z6 ~: a! h9 W
  27.     STRUCT_E_ADDR_OFFSET(s14, e_char);' g2 a/ a- c# R1 G! P
  28.     STRUCT_E_ADDR_OFFSET(s14, e_ld);
复制代码

& e7 ]' X6 Q8 h& U输出结果:$ K9 Q/ {' B' [3 t: R; G

  1. 7 s+ R  I* C, _: d
  2.   s12 size = 16        s12.e_int addr: 0028FF08, offset:  0
    9 f# G+ ~. {& w( I
  3.   s12 size = 16         s12.e_ld addr: 0028FF0C, offset:  4' q  q, O  C) c! n2 g9 z
  4.   s13 size = 24         s13.e_ll addr: 0028FEF0, offset:  07 B& q/ Q6 x9 s# G, \5 S
  5.   s13 size = 24         s13.e_ld addr: 0028FEF8, offset:  8
    2 ]6 z! x* ^' G
  6.   s14 size = 16       s14.e_char addr: 0028FEE0, offset:  08 u( ~1 w) A9 V. Z1 y1 O7 R1 R
  7.   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

  1. 4 _: ~# Z* O. |5 U+ G5 t2 \' h
  2.     typedef struct
    , l2 v; ^8 P7 a% k+ O$ n* n
  3.     {
    3 D. o; c6 t3 y
  4.         int e_int;
    1 F& `8 ]6 a; y; J- h. M
  5.         double e_double;
    % r' C$ g$ }7 R8 |4 i6 a0 j
  6.     }S11;8 q( z  z- W8 j" O% m  X% z. x
  7.     S11 s11;; _: J$ U6 J# [: {
  8.     STRUCT_E_ADDR_OFFSET(s11, e_int);
    ) \" l: m1 `5 N7 `
  9.     STRUCT_E_ADDR_OFFSET(s11, e_double);
复制代码

/ }  w# p  F& Z2 h! @
" t, ]6 B: _; z+ {" H8 g: [$ H
结果是:, [% j3 [! V0 w

  1. 2 D, S% Q* Q. l, G) J& V
  2.   s11 size = 16        s11.e_int addr: 0028FF18, offset:  0
    - q& u! x) J& z  S# |
  3.   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( @

  1. " \) I4 Z5 U" Y
  2. #pragma pack(1)
    ) W# @& F6 j) U3 E, m0 P5 c
  3. typedef struct 2 }/ W  j/ M; j. R0 a7 Y
  4. {3 s! S7 D( H. Y4 h( a2 {
  5.     char e_char;7 j8 C" F. u. x  z( R8 v9 P
  6.     long double e_ld;& y$ M0 V' `0 [4 d6 I* K
  7. }S14;# C3 U7 P% ?' ]# ^( X6 P
  8. #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

  1. ' t; d0 V* l4 K0 m1 O
  2.     typedef struct
    0 w. W# |! P  Z9 B4 _
  3.     {0 {1 ~3 W8 C8 F* Q
  4.         int e_int;
    * |% I3 U9 S$ ~7 B
  5.         char e_char1;
    & o' U$ b0 n3 |
  6.         char e_char2;; S( l. ^1 m* A2 r. g
  7.     }S2;
    , C: F* K0 e5 e# J3 I) u) \

  8.   `0 g/ G# w$ t0 I
  9.     typedef struct + Q) w, X! s( P) b% K
  10.     {, T: E2 Q) y9 X
  11.         char e_char1;
    5 V. _9 \& h# w; b0 f$ r
  12.         int e_int;3 P* u) i3 W. m/ D
  13.         char e_char2;3 b7 x8 y" o; O5 I
  14.     }S3;1 Y( r8 d1 j# D1 D7 g
  15.     S2 s2;
    * F& f% ?9 j; c6 E' _8 v( g
  16.     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 }
  1. # j4 Y" M) Q: s# U: h
  2.    s2 size =  8         s2.e_int addr: 0028FED4, offset:  0
    6 M3 W% ^3 h! Z7 C/ y) l
  3.    s2 size =  8       s2.e_char1 addr: 0028FED8, offset:  4
    2 I3 m5 @, Q( `8 i
  4.    s2 size =  8       s2.e_char2 addr: 0028FED9, offset:  52 A; i) {2 W, p" o6 f; y1 C! U( d
  5.    s3 size = 12       s3.e_char1 addr: 0028FEC4, offset:  06 b1 c4 }; _! `, k4 L' _
  6.    s3 size = 12         s3.e_int addr: 0028FEC8, offset:  4
    & d3 ^2 V: S; _) t8 s3 B/ o- r
  7.    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
微信图片_20200617125959.png
我们套一遍那几条结论就可以知道: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
  1. ( {+ v% |$ ]8 e9 m& E' U
  2.     typedef struct
    4 C% i" f* l1 ^7 t! n
  3.     {
    . ]; a% q9 t5 l
  4.         char e_char1;5 L' T- _- A# X5 b; P! O8 S" L
  5.         short e_short;
      g3 }: @* y) I" G% Z
  6.         char e_char2;
    . p) ^8 R4 T  `% N# ^) N
  7.         int e_int;
    9 H" W+ |( o$ F' b0 |. h
  8.         char e_char3;
    : k8 w& t+ q5 `) M4 i
  9.     }S4;; c3 O# x- _1 Y- ~4 n0 A0 i6 R) h4 h
  10.     S4 s4;3 h. ~  n, u8 I3 x( L
  11.     STRUCT_E_ADDR_OFFSET(s4, e_char1);
      t  N& H% t! K: Z
  12.     STRUCT_E_ADDR_OFFSET(s4, e_short);
    ( U( R$ t% J: y6 E) c! I# I
  13.     STRUCT_E_ADDR_OFFSET(s4, e_char2);5 w" A% X- E) p9 m8 |
  14.     STRUCT_E_ADDR_OFFSET(s4, e_int);
    # H( A! b3 ]- d( {! r7 {, g
  15.     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
  1. ; T4 {. W7 |5 R3 e
  2.    s4 size = 16       s4.e_char1 addr: 0028FEB4, offset:  0
    ! `  g$ T6 `$ N1 m9 o$ {& m) v
  3.    s4 size = 16       s4.e_short addr: 0028FEB6, offset:  2
    1 _2 |/ p( \6 ^
  4.    s4 size = 16       s4.e_char2 addr: 0028FEB8, offset:  4# \- e' N. l* m% P- Q8 ?9 N+ ?
  5.    s4 size = 16         s4.e_int addr: 0028FEBC, offset:  8
    ' V' @% ~& W' U" R
  6.    s4 size = 16       s4.e_char3 addr: 0028FEC0, offset: 12
复制代码
! P8 n, J8 i$ o
' E  M* d0 K" W
微信图片_20200617130003.png
得出一个经验:
# 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/ _

  1. 0 Z# I0 k% I8 J6 x- `) [" I
  2.     typedef struct
      z0 U: ~" v, F) m. Q/ e+ }
  3.     {+ p, d6 F5 |) m; L2 J: F, S
  4.         int e_int;
    , ~+ A5 h( W- C# K
  5.         char e_char;
    1 G" q' q0 e& c
  6.     }S1;
    / r5 k* w& c/ [1 {3 C* F1 P) n( U0 b/ C
  7.    typedef struct 0 g$ b# u% }& J
  8.     {
    ( F$ `6 M8 w: _$ S9 O
  9.         S1 e_s;  @8 S; q# j$ U( t8 Q
  10.         char e_char;6 t. d6 v! N2 a, P
  11.     }SS1;9 a) W& p8 D. y7 A! z9 f1 E  |

  12. ( l& v( ~' k* A( b) x+ m* g
  13.     typedef struct $ s8 O7 [8 g" J9 S3 Q$ C
  14.     {
    5 p" k2 R. Q7 n' F
  15.         short e_short;
    ( C  Q. M2 o' N3 Y# r  R2 U
  16.         char e_char;
    ; e- G, A9 g+ n
  17.     }S6;
    4 m- |  X1 b# E& y6 ]

  18. 3 r  ?1 D8 @* {% e6 ?) Q; n  l
  19.     typedef struct
    * L7 o8 Z+ s% X2 e! Y9 n
  20.     {. K; y3 p) h, B% \2 y7 W" _+ g
  21.         S6 e_s;
    ) R* l; l. V' u! F* M* M4 b: [
  22.         char e_char;
    ; s8 S+ b2 W2 ]. j9 y
  23.     }SS2;
    # H$ B6 q5 t. R0 C* ^  T
  24.    
    1 A0 H5 S$ Q  y, J
  25.     SS1 ss1;
    7 `7 Z1 m3 H2 s' Z# q
  26.     STRUCT_E_ADDR_OFFSET(ss1, e_s);
    0 P& ~3 O# D% X% ?. r' L1 x7 p6 ^
  27.     STRUCT_E_ADDR_OFFSET(ss1, e_char);* r1 O. ~7 {+ V: v& q5 z: `/ q9 Y
  28. ; r0 b) k2 g6 E% Q' e3 j8 c
  29.     SS2 ss2;) D/ y8 y0 V6 l1 V/ i
  30.     STRUCT_E_ADDR_OFFSET(ss2, e_s);
    , g) M+ N7 o/ Y( x0 y9 B; r; W
  31.     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
  1. 3 h% F3 o0 `- n1 z" w
  2.   ss1 size = 12          ss1.e_s addr: 0028FE94, offset:  0% s9 J, z& o% e" Q
  3.   ss1 size = 12       ss1.e_char addr: 0028FE9C, offset:  8
    & n, F$ Z) X- |% R/ m# F: k7 K
  4.   ss2 size =  6          ss2.e_s addr: 0028FE8E, offset:  0
    ) f) n2 [4 W2 Z; Q; O" ^
  5.   ss2 size =  6       ss2.e_char addr: 0028FE92, offset:  4
复制代码
. W4 k0 c4 U2 {8 n* f$ g
' d/ H' F8 n2 O
微信图片_20200617130006.png
得出结论:结构体内的结构体,结构体内的元素并不会和结构体外的元素合并占一个对齐单元。) 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

  1. 7 W) w1 _  m1 m5 P% i
  2.     typedef union
    2 ~4 A! w' F& V/ `# ?( C$ G0 |/ I! b
  3.     {
    & G- Z. n8 L, b* a7 p. h
  4.         char e_char;
    ! q9 i+ M$ X' [+ Q  e
  5.         int e_int;/ w6 c1 U5 u0 z$ D1 ~! b1 h
  6.     }U1;
    8 I7 A0 q- z# A/ M2 F

  7. 5 y6 ~5 Q. l# p
  8.     U1 u1;- R( f' c/ I2 T1 t9 L  Z+ w
  9.     STRUCT_E_ADDR(u1, e_char);6 V' z* q2 l3 P+ @8 l2 b
  10.     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

  1. ' i" y: B  N0 z. P+ n
  2.    u1 size =  4        u1.e_char addr: 0028FF2C
    : Y9 a0 c5 @" q! x
  3.    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
微信图片_20200617130009.png
那么,union跟struct结合呢?
  1. - u; ?# n- g  \" U% D
  2.     typedef struct
    ' W  A5 c+ G* c* m7 H
  3.     {8 h) k& S, q- ?
  4.         int e_int1;
    ( E8 w1 ]1 j! F; \4 W
  5.         union
    1 K7 w8 e! v; d. }0 }! Y$ o1 z
  6.         {
    ' o& {* n9 S! Q
  7.             char ue_chars[9]; 2 s1 l2 C9 f6 z9 M) M
  8.             int ue_int;$ g# V1 S# b6 S7 Z4 ^, ?
  9.         }u;
    8 a4 K+ d3 J. w# v1 j! E
  10.         double e_double; , G( n+ o/ w. f! M
  11.         int e_int2;
    3 h0 p" D$ R  @
  12.     }SU2;+ p0 a( _: V4 i. b# _
  13.     SU2 su2;
    3 Y# t9 K4 _' `5 D
  14.     STRUCT_E_ADDR_OFFSET(su2, e_int1);/ p, I1 _$ M) q9 I% P. ?3 ?
  15.     STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);7 ?2 b4 E1 Q' }0 U3 u
  16.     STRUCT_E_ADDR_OFFSET(su2, u.ue_int);( e* X3 c1 u. s- I7 G, P% H4 J: ?7 x
  17.     STRUCT_E_ADDR_OFFSET(su2, e_double);7 _) m% `( R0 R  X
  18.     STRUCT_E_ADDR_OFFSET(su2, e_int2)
复制代码

) p# @* i" ~+ ~& f
输出:

  1. * P8 [4 A8 P0 r; _! ]0 I" B$ C
  2.   su2 size = 32       su2.e_int1 addr: 0028FEF8, offset:  0
    1 i) P; w0 g9 s$ i9 q
  3.   su2 size = 32   su2.u.ue_chars addr: 0028FEFC, offset:  4
    + T0 C% @- M% D$ J* Z
  4.   su2 size = 32     su2.u.ue_int addr: 0028FEFC, offset:  4, L2 `1 ^$ u6 E$ U) q
  5.   su2 size = 32     su2.e_double addr: 0028FF08, offset: 169 W/ W" @7 r& t' N6 c7 q
  6.   su2 size = 32       su2.e_int2 addr: 0028FF10, offset: 24
复制代码
$ T7 |$ H4 k; M3 d
微信图片_20200617130012.png
实际上跟结构体类似,也没有特别的规则。
顺便提一下,使用union时,要留意平台的大小端问题。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
百度百科——大小端模式
怎么获知自己使用的平台的大小端?Linux有个方法:
  1. 3 v/ O3 j1 X5 i  f0 g4 y- S
  2.     static union { ! {$ s# V- a: v$ }
  3.         char c[4]; , _- T* K) L: _, I8 C* H
  4.         unsigned long l; 9 r0 c1 y/ y& f9 A% v+ @7 }
  5.     } endian_test = { { 'l', '?', '?', 'b' } };" Y  ?: @% ?: l: K! W( H" h0 R
  6.     #define ENDIANNESS ((char)endian_test.l)" O8 r' B; {5 V3 h" f# ]
  7. ! h) C7 @/ a2 V% @0 H; N
  8.     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
直接看个测试代码,就可以明白:
  1. 7 x' t6 V5 g% o! o
  2. void bitfield_type_size(void)
    8 E% ^# m  ^& E3 z: b
  3. {9 }. k) r* s# W6 \- F" ^4 e1 e
  4.     typedef struct
    % M6 E$ E! @8 v) i" r$ O
  5.     {
    ! m% G( Q+ P- [$ e
  6.         char bf1:1;
    5 V3 [* G: N" x7 `% ?5 ~7 d
  7.         char bf2:1;, N5 A$ `  w/ E: ~
  8.         char bf3:1;
    5 R* K0 I7 Y  Z. {# j1 Y
  9.         char bf4:3;" r. B! l! P3 w4 ]# ]8 t, X
  10.     }SB1;3 n& Q4 i" S) O$ y5 ?8 Y3 M8 i; ?: n. W

  11. ' m7 N$ _% a# U: @
  12.     typedef struct
    ) L/ n! Q3 |$ d; v
  13.     {# D" x) I7 w8 A0 w* z; C9 T3 {1 N4 S% q0 P
  14.         char bf1:1;9 Q0 D1 ?6 z3 F# u- R; B: ^7 [
  15.         char bf2:1;
    : X3 [2 U( d! ~+ h/ \1 S; S
  16.         char bf3:1;
      M& P, s' S- F$ S
  17.         char bf4:7;2 ]. e9 v) K; ^  [* ~' C
  18.     }SB2;
    3 x7 j# v' i% \/ C) \  T8 F
  19. ( U8 i6 \, o" p3 X
  20.     typedef struct" m# e0 B0 ?; [0 ]& h2 j# [( k
  21.     {( X0 D8 o+ X; i1 `1 o. f
  22.         char bf1:1;
    / ]  c" A4 x( Z& ]
  23.         char bf2:1;8 R; F) a  B! Z) P9 G
  24.         char bf3:1;  l2 k$ s0 x/ B( ~7 I; L+ `2 y
  25.         int  bfint:1;. q0 v  B0 y9 O( a8 ?
  26.     }SB3;$ J$ `: a( b6 h1 g
  27. ; m( s% o1 m: W" O. G/ W" {( S
  28.     typedef struct/ A! A( B- u0 u1 p7 }
  29.     {, z) P7 R3 ~5 V. v) I
  30.         char bf1:1;
    $ U! E* H; _% v& v2 Y
  31.         char bf2:1;& f/ |+ g  ^2 V+ o4 j
  32.         int  bfint:1;
    7 g* k$ N3 c+ R# w
  33.         char bf3:1;
    0 u" z) P; N# O
  34.     }SB4;
    : u6 x) {3 P( J) K: k5 e, j" a

  35. . }1 R/ j' F9 Q% Y6 s" R
  36.     SB1 sb1;
    ' _. @! a: m# m3 C3 I7 Q; F# ]
  37.     SB2 sb2;
    8 b  [# V, a/ [5 ?6 S
  38.     SB3 sb3;
    - Q8 I" `1 z6 [  k; \$ o
  39.     SB4 sb4;, \* T$ K  e1 K0 t  I2 S" a$ J( O
  40.     VAR_ADDR(sb1);
    ; r2 N# F  \9 N4 W7 k2 ?
  41.     VAR_ADDR(sb2);, W* n$ \$ O; F  p2 f/ F1 T
  42.     VAR_ADDR(sb3);& ]' A3 m) ^5 s) N) }5 ^% k  J
  43.     VAR_ADDR(sb4);
    & D% t' C. c  T# W4 x5 I; B
  44.     # ~& A5 x* Y7 |8 Z: s
  45.     typedef struct6 J8 Z& o6 ], W
  46.     {- }8 I; f  [* D- u( a: ^
  47.         unsigned char bf1:1;  d) y' l# U( l0 R) @1 M8 ^
  48.         unsigned char bf2:1;0 t* \9 \- y1 h0 E2 [) a2 X7 m
  49.         unsigned char bf3:1;
    0 y- V% h8 U( a' E
  50.         unsigned char bf4:3;% J5 ^$ R% a1 I: G
  51.     }SB11;) y0 r5 F7 h9 d; r

  52. ( F, y+ w: s* i; ?2 N) D0 B
  53.     typedef union 2 k4 [: E4 r' n1 e( v, D  Z
  54.     {3 @, V$ s' x; ~, V2 X0 r, `& N
  55.         SB11 sb1;' V9 \" D9 Q, |% p: S, G8 E% x( c
  56.         unsigned char  e_char;$ E4 W4 t5 s& \# Q, {8 W
  57.     }UB1;
    # I, S8 b/ |8 v9 u, S
  58.     UB1 ub1;
    3 L* O( x5 a% ^: [+ s" f" P9 ~$ z8 \
  59. 0 m8 I" l2 i  z$ ~: }  s0 j9 ~
  60.     STRUCT_E_ADDR_OFFSET(ub1, sb1);! B! h7 j" L" z  m  u
  61.     STRUCT_E_ADDR_OFFSET(ub1, e_char);6 @3 a+ C9 m# ~, f% ]8 ?8 N$ x
  62. # D! `8 @! _. j: v( n3 }/ O7 \
  63.     ub1.e_char = 0xF5;
    " x/ Y) c, _3 r0 K1 `
  64.     BITFIELD_VAL(ub1, e_char);
    $ o8 B+ Q3 A/ R( Y! V% l
  65.     BITFIELD_VAL(ub1, sb1.bf1);8 C, z# M, ^( [. D, l5 t. x7 W& p/ ]
  66.     BITFIELD_VAL(ub1, sb1.bf2);
    ! i5 B5 U9 h; g; w
  67.     BITFIELD_VAL(ub1, sb1.bf3);2 Y3 [% s% `. S& X. K
  68.     BITFIELD_VAL(ub1, sb1.bf4);
    3 c  m. O, t% }" p3 K% S3 X
  69. }
复制代码

& I5 y4 `8 q; B( C$ L4 v
输出结果是:

  1. % j, I' j* j  I# }$ u
  2.   sb1 size =  1        sb1 addr: 0028FF2F
    ' }* q1 Y9 h% _/ w/ F! x) |; s
  3.   sb2 size =  2        sb2 addr: 0028FF2D
    9 Y- I3 U( ~: ?- e
  4.   sb3 size =  8        sb3 addr: 0028FF24& D. F8 Y( y+ U* O. C
  5.   sb4 size = 12        sb4 addr: 0028FF18
    1 O5 {: C$ c8 @9 B5 [" H
  6.   ub1 size =  1          ub1.sb1 addr: 0028FF17, offset:  06 m, h' l" z6 i3 F( H8 F$ D
  7.   ub1 size =  1       ub1.e_char addr: 0028FF17, offset:  0' g% g+ K$ z' k) F5 {
  8.          ub1 :  1 Byte, ub1.e_char=0xF5
    + u; b1 {; m* H9 }! A' e1 p
  9.          ub1 :  1 Byte, ub1.sb1.bf1=0x1
    : p9 a1 k& W* y
  10.          ub1 :  1 Byte, ub1.sb1.bf2=0x0
    ( c6 G! O: r9 f7 C8 {
  11.          ub1 :  1 Byte, ub1.sb1.bf3=0x1* J5 B/ N( L9 y* w" _- |8 S; _
  12.          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
  1. " [( T4 S4 d* _) ?" ^" Q
  2.     typedef struct
    0 P2 e$ G! U7 v9 ]9 N8 W
  3.     {9 \9 t$ L: u7 W8 ~& z7 n: A$ x
  4.         int e_int;: _8 f2 K6 L! M4 G
  5.         char e_char1;9 w4 |- n3 `1 B6 V$ r
  6.         char e_char2;8 F9 b$ M/ Y; O! T1 K' X5 r
  7.     }S2;
    % k* `9 b5 S( @+ L
  8. % _0 Z3 e5 o6 s5 o6 Q* c/ i- L5 b  Z
  9.     typedef struct
    2 X  D" G* s5 i: s4 @% N$ H; ~
  10.     {! V( ^. a9 n* V% e1 E0 Q
  11.         char e_char1;
    $ {6 J* T# \6 Q+ J+ c7 A1 @$ Q
  12.         int e_int;& E  T5 J% v8 x, b. ?4 Q+ i$ g
  13.         char e_char2;
    9 C* \0 C3 x1 y6 `3 Z
  14.     }S3;
    $ r* R/ ]  h0 n2 ^6 h
  15.    
    ; K% m' Z0 F" r
  16.     S2 s2[1024] = {0};0 X  G: Y4 i; c+ I
  17.     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
即,通信时候,通常使用数组参数形式接收,而处理的时候,按照预定义格式去访问处理。例如:
  1. + L, S6 g% i- j
  2. U8 comm_data[10];
    % Y. r- O! J# ?" _; X
  3. typedef struct- ^' v  {+ v" w' B$ {
  4. {
    ) M* D$ X. Z& x5 D2 {" o- S
  5.     U8 id;. u) S8 ~( f* |: U! d6 d0 C1 j
  6.     U16 len;; s9 [" z, v) r  G' g2 z' Y8 C- e3 @2 b
  7.     U8 data[6];
    4 z1 F5 S) O$ i' X% o
  8. }FRAME;9 q/ B7 |: F3 P/ [

  9. / @* Y1 T. @4 [; U" B5 q
  10. 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位的,各种参差不齐都在里面。那就要考虑结构体内存对齐了,特别是结构体内有不同类型的元素。
微信图片_20200617130015.png
这里只探讨应用场景,具体实现还要根据实际情况来定义。

/ y6 j; x# ?* k! A; c

▍测试源码
  1. + P2 r# X. }/ T' R6 Z, [5 y
  2. #include <stdio.h>$ g8 \: b6 d+ K( R9 `# r

  3. 1 z* D4 w9 U% f: e. b9 w7 T
  4. #define offset(type, member)      (size_t)&(((type *)0)->member)
    ' E' l. Y5 \& k* h* {

  5.   _: w2 T" M/ M5 C+ Q& d
  6. #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 `
  7. #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
  8. #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
  9. #define VAR_ADDR(v)                 printf("%5s size = %2d %10s addr: %p\n", #v, sizeof(v), #v, &v)
    # h" N4 x/ M: H  X: \
  10. // #define BASE_TYPE_SIZE(t)   printf("%18s = %d\n", "sizeof("#t")", sizeof(t))
    , ?! `  C1 R" X: B
  11. #define BASE_TYPE_SIZE(t)           printf("%12s : %2d Byte%s\n", #t, sizeof(t), (sizeof(t))>1?"s":"")
    ! n3 v7 Q7 y! s. X
  12. #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
  13. 5 M0 |9 y! `5 h( s( X0 {1 }
  14. void base_type_size(void)
    1 ^& `6 q  s1 K9 v3 |/ o+ k# D% M
  15. {
    4 c" P0 p; U% [+ o2 i3 {
  16.     BASE_TYPE_SIZE(void);
    & t% w3 J2 n- w1 V/ ~- \' i# ?6 T
  17.     BASE_TYPE_SIZE(char);( g. b1 c& J/ I4 [$ x0 U( v
  18.     BASE_TYPE_SIZE(short);5 o  n; x. l% H# H
  19.     BASE_TYPE_SIZE(int);: |% P+ `8 _+ N  m" w* F
  20.     BASE_TYPE_SIZE(long);& _8 G3 y; a: m! G+ g( c- e' u9 A
  21.     BASE_TYPE_SIZE(long long);
    - V; f+ I6 N- X* Q0 j$ z* l
  22.     BASE_TYPE_SIZE(float);; t( B7 k- ^+ H! S9 X6 s
  23.     BASE_TYPE_SIZE(double);: F0 J, Q1 N# v2 R  c& |
  24.     BASE_TYPE_SIZE(long double);
    5 k$ [* C: O8 r, K0 O
  25.     BASE_TYPE_SIZE(void*);
    - T0 V) O" ~$ J4 w1 R$ i
  26.     BASE_TYPE_SIZE(char*);
    8 m; h! ^1 N7 \
  27.     BASE_TYPE_SIZE(int*);
    * D  L% e$ a: ^  V$ n% o  S7 {4 _3 I( z
  28. }
    : ^! O3 @" F3 {6 s4 |- S
  29. void struct_type_size(void)7 j% K- B/ N" L0 C  U! O& _  q1 h
  30. {9 o# t) l  J* ]+ V
  31.     typedef struct
    $ ^. E4 z8 X& u. Z! z- S
  32.     {
    8 D7 G1 @# ~5 A5 m( A
  33.     }StructNull;# w$ L: ]5 n, x+ }# n

  34. + A. t  [$ ~5 \, `. }" g8 G
  35.     typedef struct
    2 [: O3 H# G' A5 u
  36.     {
    0 ]9 ~: M7 k1 r8 w
  37.         int e_int;# J" \, T$ R  ^$ \1 G) B
  38.         char e_char;
    1 S4 w8 g: J. P( r9 p/ K
  39.     }S1;1 P4 k6 T& w0 x) x
  40.     BASE_TYPE_SIZE(StructNull);
    2 {& g: k4 u( Y2 N% ?4 c
  41.     BASE_TYPE_SIZE(StructNull*);' }1 |. B+ o4 c7 {$ D3 B! r
  42.    
    / l" T; G. y3 a+ ^) U
  43.     S1 s1;
    9 z; ~. j. U, b& F" a
  44.     STRUCT_E_ADDR_OFFSET(s1, e_int);2 }! s" a7 w( V, p5 V! }, _
  45.     STRUCT_E_ADDR_OFFSET(s1, e_char);
    5 ~! O  a- T) E. |

  46. $ w3 I8 n1 l# ?/ z- M1 q
  47.     typedef struct
    $ f. O. F' N; \( _% B+ S
  48.     {  d# f0 n( d) }( t( r2 I
  49.         int e_int;
    % R- l, V% ^+ d* Y" L
  50.         double e_double;
    + S+ ^3 d  O2 u0 D) q7 q8 M
  51.     }S11;
    4 T$ N- Z- ]( z) [3 s
  52. $ u8 b4 s9 s: n- {9 w  N0 @
  53.     typedef struct 8 y# B# Q$ |8 O% q5 K
  54.     {
    # O+ L: I' P% K* Y1 Y5 V7 F
  55.         int e_int;+ C  _# r! S8 q8 b2 B9 @& x) Z$ b
  56.         long double e_ld;2 Q" v6 E+ `# @! `- j# o- D
  57.     }S12;
    % }; d6 J. N& \, }% K& S8 J

  58. ; k3 [. g, \1 T2 n1 t, K
  59.     typedef struct , {5 P5 L$ m2 I2 s; d
  60.     {
    ; C0 U+ L; a9 y0 p2 \! q
  61.         long long e_ll;( O1 c2 j7 r; G1 `* f
  62.         long double e_ld;: ~6 W+ z. K( S2 n
  63.     }S13;
    8 N; v' B: f6 [2 \  u* `
  64. * R( D( a# o: ?" c/ X( d9 I
  65.     typedef struct
    $ h9 x* C" J6 Q0 v8 i+ g: z0 `
  66.     {: I  O- a  I! J. I6 _. Y9 p
  67.         char e_char;
    6 j! O: M3 p: K. V
  68.         long double e_ld;0 y: C2 L+ f- z3 u. e$ p# J) R2 U
  69.     }S14;
    5 H$ [; }+ Z4 Y3 r5 O0 {+ `

  70. 9 m' r% i8 ~& w+ X; S- g; F# p
  71.     S11 s11;
    % s8 i$ N: z' D
  72.     S12 s12;- W0 l  ^6 A7 Q3 U2 U
  73.     S13 s13;
    8 r/ M& W& z. H. R
  74.     S14 s14;
    0 P( d- V; [) z- H% W3 R! ^1 W
  75.     STRUCT_E_ADDR_OFFSET(s11, e_int);" S" [  {* g3 f" J& I4 g
  76.     STRUCT_E_ADDR_OFFSET(s11, e_double);& i1 t* l7 q1 M' F
  77.     STRUCT_E_ADDR_OFFSET(s12, e_int);  c" V8 R+ S5 P. f$ y
  78.     STRUCT_E_ADDR_OFFSET(s12, e_ld);) x. W3 c' A5 N3 r! b8 _" z5 j; @
  79.     STRUCT_E_ADDR_OFFSET(s13, e_ll);1 ?' {. Y' C+ l' z
  80.     STRUCT_E_ADDR_OFFSET(s13, e_ld);! D/ x1 C8 ^& s& r; T2 w
  81.     STRUCT_E_ADDR_OFFSET(s14, e_char);
    $ H; o* ^1 W4 G
  82.     STRUCT_E_ADDR_OFFSET(s14, e_ld);4 Y" L- P% P4 O" `+ t

  83. 6 U$ [4 J1 m1 F( r# Y9 {' b! x
  84.     typedef struct
    . }: |: M' n) Q* l7 f3 l0 Q1 `
  85.     {
    ! W. H. ?4 `5 S8 w' k5 N& o. J9 B+ M
  86.         int e_int;
    0 k) v8 l, a# P$ L2 n0 \. Q0 C' a
  87.         char e_char1;) c: c0 S% V" H, Y% b4 w
  88.         char e_char2;
    # x6 N0 r4 ]$ z
  89.     }S2;
    6 G' I2 O9 j4 {- O1 l# v) K
  90. 2 k5 H) G+ Q5 d) J7 R+ }( w, ^# p
  91.     typedef struct
    : x/ t3 X( Z: ?7 P  i
  92.     {: Q& V0 m/ B4 Q
  93.         char e_char1;
    ! f8 P# V4 l& s+ x" k
  94.         int e_int;  [8 I4 Q3 g) E. T; _
  95.         char e_char2;
    / t5 Q( Y! d( W- z1 }1 j3 R
  96.     }S3;! d. h) t+ R8 j  s( o; R
  97.     typedef struct
    . i4 K4 N0 v8 v( B
  98.     {1 T, J1 {7 t, H7 l
  99.         char e_char1;( s) L# l1 u. e6 {) [2 g8 O7 p* Z4 Z) [
  100.         short e_short;
    . N7 w$ [4 k9 n* Y6 N( p* l8 P
  101.         char e_char2;
    % p  N# x; L. U4 P
  102.         int e_int;% c) A5 w9 i$ ?
  103.         char e_char3;% b* i4 i' o! F5 {$ D
  104.     }S4;
      P8 P6 a4 u7 {: z5 P6 k
  105.     typedef struct
    . k" Q' n$ C4 T" L$ k5 f4 @0 Z' _: w$ @
  106.     {
    - b  C6 Z2 a* ]% Z4 c% E. M1 Y
  107.         long long e_ll;0 }6 X) v! i6 i3 p8 h
  108.         int e_int;' m8 x% s. \' S( Q! p$ c$ |0 Q! @. k
  109.     }S5;$ U! ]2 d2 W( t6 R! n) w' O

  110. 0 N: Y" ]' Z! \* p
  111.     typedef struct ( i! \9 l- _( G+ q: D# h
  112.     {4 q% [  P7 ~8 C6 w; s6 Q
  113.         S1 e_s;
    & T0 q5 U9 E8 ?4 F# @6 b7 Q
  114.         char e_char;" y) d1 F( A! N' |  N
  115.     }SS1;
      J) Z3 t% d) q* _. U' `+ ~  h

  116. $ u7 y+ Y, I) f1 p- a! E. R% ?
  117.     typedef struct ; F- p* o, W, t( B0 D  I' s1 H
  118.     {
    7 B. n* N$ h9 W& q. \% {/ n
  119.         short e_short;
    6 h! U% d( d" G, o4 g
  120.         char e_char;1 a5 r* j$ A' ^; a/ V
  121.     }S6;- g" W/ q5 A, h; u: Y- {# S, A

  122. + ?6 d9 |8 M$ l  `' w4 h* i  l- f
  123.     typedef struct
    8 ^) v) H; g$ \9 W- `5 c3 M
  124.     {6 L1 r2 ^$ |( }6 I) l* M  @0 Z( f
  125.         S6 e_s;
    2 P. |- G: d. N$ C) r$ N& r1 P
  126.         char e_char;
    + ?, F  m; [- O
  127.     }SS2;
    % h, I/ t  }7 n" j

  128. ! Y; s: M* _# N1 o6 ?) ^. M

  129. $ x1 X5 E( M# y  g; W0 \+ s8 z
  130.     char var1;
    % [. S) D8 q% w7 h1 G" a% ^7 l
  131.     S2 s2;
    4 Q8 d2 q) Q( E; c4 A/ U, N
  132.     char var2;4 L7 l$ u% F" k( w
  133.     S3 s3;/ s) [- |* `' {' x$ O3 u1 W
  134.     VAR_ADDR(var1);
    # z- j# Z0 n0 K/ Z4 i+ k" ~$ x4 ]
  135.     STRUCT_E_ADDR_OFFSET(s2, e_int);2 N; D; V2 f8 m6 t& J
  136.     STRUCT_E_ADDR_OFFSET(s2, e_char1);
    9 D3 a% W& P4 f, N7 O3 V5 i
  137.     STRUCT_E_ADDR_OFFSET(s2, e_char2);5 m( e9 \+ T# t! a
  138.     VAR_ADDR(var2);* l4 v) c  d: `: T
  139.     STRUCT_E_ADDR_OFFSET(s3, e_char1);& W! S* K: R2 D: p1 k, `( |* u: b
  140.     STRUCT_E_ADDR_OFFSET(s3, e_int);* ]. N/ n+ L% v$ F
  141.     STRUCT_E_ADDR_OFFSET(s3, e_char2);. g! ]2 Y1 Z* ]5 V7 e& H$ ~
  142.     S4 s4;5 l' S% a5 Q* J0 y/ W  V$ M4 R0 X
  143.     STRUCT_E_ADDR_OFFSET(s4, e_char1);5 i+ m  z$ l$ N2 T+ A& {- d0 T
  144.     STRUCT_E_ADDR_OFFSET(s4, e_short);% V; z+ S5 f: a
  145.     STRUCT_E_ADDR_OFFSET(s4, e_char2);
    . v: d3 i, ~, v3 q5 y% i
  146.     STRUCT_E_ADDR_OFFSET(s4, e_int);8 b8 A0 |. K! T5 I6 Y  v
  147.     STRUCT_E_ADDR_OFFSET(s4, e_char3);
    ; _* l1 Y/ l7 r
  148.     S5 s5;0 u  B5 d* @1 I6 W  r+ j! ~- D
  149.     STRUCT_E_ADDR_OFFSET(s5, e_ll);
    7 ]3 D; I! U- v- K& I- y% v$ j
  150.     STRUCT_E_ADDR_OFFSET(s5, e_int);1 ^3 S* g; G& {% n6 ?3 G' M
  151.     SS1 ss1;# _1 g; E# g0 o
  152.     STRUCT_E_ADDR_OFFSET(ss1, e_s);
    # {1 S0 E: z! o( \
  153.     STRUCT_E_ADDR_OFFSET(ss1, e_char);
      K- B1 B& u$ w( l( I$ v6 R# a4 ^

  154. " w) s& |; b' {' U$ _
  155.     SS2 ss2;
    * J' }$ }9 A$ `( h5 a
  156.     STRUCT_E_ADDR_OFFSET(ss2, e_s);) J8 J& P" h+ W) U+ R4 }  W
  157.     STRUCT_E_ADDR_OFFSET(ss2, e_char);
    ) M/ R7 W! ?+ w# `! U9 T, x
  158. }! n  k3 X! n3 W( {
  159. + u) p; Z0 z! `# a
  160. void union_type_size(void)
    + E7 v) ~5 e! b! m" U5 r
  161. {
    8 d$ b4 E/ t6 w9 }& }: G
  162.     typedef union $ ?: r2 n, K- X
  163.     {
    & A7 Z6 [% A6 \) Q1 P: g& [- y  y
  164.         char e_char;0 E1 o; P& C) n4 ^! q
  165.         int e_int;2 ]6 _( i* j: s1 ]8 P# H
  166.     }U1;0 ^: U' T  z+ H) Y+ V# P2 e

  167. 1 T2 k1 i% [, B' S
  168.     U1 u1;
    + _8 ^# x9 n4 k4 D
  169.     STRUCT_E_ADDR_OFFSET(u1, e_char);
    ( \4 O# f8 a1 S: e! J
  170.     STRUCT_E_ADDR_OFFSET(u1, e_int);4 s) k1 l! ?. Y! U# p: L
  171.    
    ; A5 V5 x& M+ s1 s& |
  172. 1 }% r/ d# X- w2 J6 o# T
  173.     typedef struct6 F: {4 J. e0 }, c# ~; X8 O
  174.     {
      L3 f. j7 ]9 x# v. F+ ~
  175.         short e_short;
    $ t0 c6 Z  b# |+ `- ^
  176.         union6 g8 I# d8 B! S6 {6 ~
  177.         {
    - n2 M' n7 @6 n% X
  178.             char ue_chars[9];: z5 M: ^& N% r' O1 G
  179.             int ue_int;& A# @2 D1 U2 O1 N6 Y+ N  \
  180.         }u;
    4 H& J9 M, a5 a% j
  181.     }SU1;0 C2 \5 B& B+ y7 T0 B

  182. $ g) \& v. v" J# q* l/ f- M! p
  183.     typedef struct
    : z  Y' \# k  ~8 P- L, b# ~1 z) _
  184.     {1 o9 f/ y+ U" J' Q
  185.         int e_int1; 7 _! M( W) ^6 h2 X
  186.         union
      t. y% S, g/ q4 R* e8 W4 z
  187.         {
    / [6 E1 n; `$ F2 U2 q
  188.             char ue_chars[9];
    ( j% H' v( [0 m" A5 |
  189.             int ue_int;8 ~+ ]0 J" Q. Z8 n3 l- m# i( o
  190.         }u;
    4 f# `# h4 C: s  B* j- @% E1 _7 o
  191.         double e_double;
    / L! ]; G+ I3 d6 p: ]4 J4 E
  192.         int e_int2;
    % a7 E$ b3 j3 n" d
  193.     }SU2;
    0 E- m+ Z4 x4 Q9 ?: N# a
  194. 2 x% @  L9 r( y4 G( `" ~* j) ]* r- [
  195.     SU1 su1;
    0 q( X5 @# o+ a3 c. t' q4 ]4 d
  196.     SU2 su2;
    6 N, k6 g5 ?+ [& x+ W2 D* e% }  Q
  197.    
    ; Y% `& m7 R2 W8 K6 L1 I" G1 d# |- R
  198.     STRUCT_E_ADDR_OFFSET(su1, e_short);
    6 m; ^( @1 H/ g; Y% z' g
  199.     STRUCT_E_ADDR_OFFSET(su1, u.ue_chars);
      `* e  S: T* S  d3 r
  200.     STRUCT_E_ADDR_OFFSET(su1, u.ue_int);3 o$ i6 d% M5 ^" E0 b
  201.    
    + |! U( t( q, m9 U( ?
  202.     STRUCT_E_ADDR_OFFSET(su2, e_int1);% z3 [6 k3 F+ ]' O5 L3 u
  203.     STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);
    . ~/ A4 r# R2 P
  204.     STRUCT_E_ADDR_OFFSET(su2, u.ue_int);/ q% j; E2 L, B+ w% x
  205.     STRUCT_E_ADDR_OFFSET(su2, e_double);
    8 Q, Y7 y# }6 `  p! }# ?, H
  206.     STRUCT_E_ADDR_OFFSET(su2, e_int2);
      _: d3 B; i: T6 B! \4 ~6 X; K. h* z
  207. }. N0 s; o" |( c$ o/ R/ H$ a

  208. 4 _5 n* h8 P( F' P/ h* F
  209. void bitfield_type_size(void), G( P% z: }, S1 G1 |
  210. {# @" A! S- F, s$ J( h: m
  211.     typedef struct4 \  n$ i( [. s( _
  212.     {
    7 \% S  G+ N8 l: Y
  213.         char bf1:1;
    8 ?: [% X3 E" i0 ?3 j' T: B& R( r0 b
  214.         char bf2:1;( h4 K8 f$ d" r: O8 j0 m/ r! ~
  215.         char bf3:1;- f- r4 p) p/ H9 V
  216.         char bf4:3;9 a8 s' N+ E5 }* R5 B; r- Z
  217.     }SB1;
    , ^! n1 |" X6 a
  218. - G5 v7 |# {; J1 L; z$ ^
  219.     typedef struct
    " S  ?9 T4 d, b2 G5 O) z4 [
  220.     {
    $ u# U4 I: {/ |2 }
  221.         char bf1:1;, O; x: E1 ^5 m- E6 ~, C
  222.         char bf2:1;
    # C9 k& X. A$ }2 l
  223.         char bf3:1;
      F! P7 ?; v: y& x
  224.         char bf4:7;$ a$ X( E: @( Z& z. \7 M
  225.     }SB2;. |) c% _# F' A" w( C% t0 S
  226. + [5 u" X7 k/ K4 R$ ?. \" ^) X
  227.     typedef struct, q* I- G/ y8 H
  228.     {" A; ]& G* _( e. I7 T3 p
  229.         char bf1:1;
    ' {; `( d: _; q' ?( \
  230.         char bf2:1;6 J, H: s5 F6 J3 O- V1 T
  231.         char bf3:1;* }% z  n  ^; s' ^0 h
  232.         int  bfint:1;2 Z! h) J8 Y7 Q) [' K  \; C& Q
  233.     }SB3;
    - y; B$ w4 ]* }+ ^4 y/ t

  234. . B9 g8 m, l) K2 K
  235.     typedef struct( u' I/ W! i! @$ w* U
  236.     {) H# E1 B6 m. W. O
  237.         char bf1:1;
    & ~/ F1 u! x3 Y+ I  ~# y5 B/ Y  [
  238.         char bf2:1;. t. M: n/ _$ r5 Y( o! Z
  239.         int  bfint:1;0 O* \' {: {6 G$ i2 S2 Y" ^
  240.         char bf3:1;
    $ Q: J" {$ l% e, E* i# e
  241.     }SB4;& b2 Y7 f0 V1 B1 t5 x& k. P
  242. / e$ W( k' s" C7 V6 g. f
  243.     SB1 sb1;! d6 [3 C! U7 R- F: {& |" O+ g! [; q
  244.     SB2 sb2;
    4 n! t; \. K$ M9 Q
  245.     SB3 sb3;+ g! Y0 N$ L! B
  246.     SB4 sb4;1 {6 ~$ J  }" j, w% l. R
  247.     // STRUCT_E_OFFSET(sb1, bf1);
    4 x3 S+ J! K9 f
  248.     // STRUCT_E_OFFSET(sb1, bf2);
    + b( V5 [3 J, ~1 n" R5 S
  249.     // STRUCT_E_OFFSET(sb1, bf3);
    % i' h/ @& R+ ?  |- l0 X1 m
  250.     // STRUCT_E_OFFSET(sb1, bf4);9 y! a" A$ x, o9 y
  251.     VAR_ADDR(sb1);
    4 [+ B: B* g7 w. r1 Z9 L. d
  252.     VAR_ADDR(sb2);
    - G$ M( o! i: L
  253.     VAR_ADDR(sb3);
    0 j6 `" t  r9 p4 [5 }+ A
  254.     VAR_ADDR(sb4);4 X& t& {$ v) @# O/ J/ H
  255.     * U; c) U  v  H: `; m
  256.     typedef struct
    8 ?& \5 B& E: G2 J) o
  257.     {
    / l9 \' @3 p6 W7 D2 {% M
  258.         unsigned char bf1:1;0 g' V6 }# i3 ~# o7 V; |% a
  259.         unsigned char bf2:1;) w, [7 ~' a! l5 V1 M' m. P
  260.         unsigned char bf3:1;
    $ x7 y% H. H) [3 k. H! y  C
  261.         unsigned char bf4:3;, x! ~3 J3 X, h5 e# A
  262.     }SB11;- S$ a8 D2 l8 u" ]6 n( ?9 d, q0 w- a2 b

  263. 4 s# b; w( C2 I% O( ~  y
  264.     typedef union 6 D/ H, a8 c' Q1 A
  265.     {2 }% @* u. j* L) u* t& b2 @
  266.         SB11 sb1;  q6 f' n# x$ M2 A( Z
  267.         unsigned char  e_char;
    3 G2 |: {& }5 x) K/ S2 A& q
  268.     }UB1;
    # d  p4 R4 L( L2 N9 ^( `* S
  269.     UB1 ub1;" m6 w, Y. V  p' V, P3 P
  270. ) y6 @& C3 d# Z: L! [& M- z
  271.     STRUCT_E_ADDR_OFFSET(ub1, sb1);
    ( d2 Y& J0 s; \; q- x
  272.     STRUCT_E_ADDR_OFFSET(ub1, e_char);
    3 z' I: M9 |" u
  273. . m" s( \+ j8 a. R8 m9 k6 z
  274.     ub1.e_char = 0xF5;
    ! m9 b+ V8 s4 a+ G, `$ Q
  275.     BITFIELD_VAL(ub1, e_char);/ _: G: `0 O& D6 n/ s! P: Y
  276.     BITFIELD_VAL(ub1, sb1.bf1);0 i# V/ Z! I3 Y7 @9 @3 @
  277.     BITFIELD_VAL(ub1, sb1.bf2);
    6 C1 b: r2 ^& v6 R
  278.     BITFIELD_VAL(ub1, sb1.bf3);4 C$ D" }4 N! H9 g+ t# K6 m, C
  279.     BITFIELD_VAL(ub1, sb1.bf4);2 D- m/ B9 @5 s# i: |" j" `

  280. ! K. Q( _: u9 g+ _$ d# H' i4 Z8 y- Q
  281.     static union {
    ( ^$ e1 S: u$ b5 I" i: [4 |/ s
  282.         char c[4]; ) h& O: s1 k# F8 m
  283.         unsigned long l;
    0 [  E# Y- d% `5 K1 T8 A2 o
  284.     } endian_test = { { 'l', '?', '?', 'b' } };  H2 `  Q- m0 m( U+ V1 k
  285.     #define ENDIANNESS ((char)endian_test.l)# q! V& O) o9 ]$ u
  286. " ~9 D7 p1 I+ w9 L/ f* z
  287.     printf("ENDIANNESS: %c\n", ENDIANNESS);* w" ~6 H( E3 u( H& T2 U$ ]+ g

  288. ) k, a8 {) I# T3 z) k& K
  289. }1 K/ S- y* A2 I1 h
  290. int main(void)
    , X, ~" E. v( i4 C; k: H
  291. {) j0 _$ B" o  A' ^7 G% r
  292.     // base_type_size();
    ; X0 |! H7 h7 g  D! e3 \
  293.     struct_type_size();" }' P0 K+ _7 _! r7 }2 x
  294.     union_type_size();
    " T8 \, _, M1 G* m/ h2 C
  295.     bitfield_type_size();; R$ U: J; ~9 x% F

  296. : J: p) X# J; \* z0 d( J/ l
  297.     return 0;, `3 g* Q5 n( p! h7 g$ h, q
  298. }
复制代码
+ N" Q; B: G9 A' w

' j: I+ K$ f8 x2 q
赞 收藏 评论0 发布时间:2020-6-17 13:01

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版