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

C语言结构体对齐问题

[复制链接]
gaosmile 发布时间:2020-6-17 13:01
C语言结构体对齐问题,是面试必备问题。
本文,除了用图解的方式讲清楚结构体知识点外,还将为你解答以下问题:
  • 为什么会有结构体内存对齐?
  • 结构体怎么对齐?
  • 学习结构体对齐有什么用?
  • 结构体对齐有没有实际应用?


▍结构体内存对齐的原因

一句话,为了提高效率,这个跟芯片设计有关。
自从我们刚学习编程开始,就会接触到例如字、双字、四字等概念这里涉及到内存边界问题,它们的地址分别是可被2/4/8整除的。另外,在汇编中,不同长度的内存访问会用到不同的汇编指令。如果,一块内存在地址上随便放的,CPU有可能就会用到多条指令来访问,这就会降低效率。对于32位系统,如下图的A可能需要2条指令访问,而B只需1条指令。
微信图片_20200617125952.png
微信图片_20200617125955.png

▍结构体内存对齐的规则
1. C语言基本类型的大小
不要瞎猜,直接上代码。每个平台都不一样,请读者自行测试,以下我是基于Windows上MinGW的GCC测的。

  1. #define BASE_TYPE_SIZE(t)   printf("%12s : %2d Byte%s\n", #t, sizeof(t), (sizeof(t))>1?"s":"")
  2. void base_type_size(void)
  3. {
  4.     BASE_TYPE_SIZE(void);
  5.     BASE_TYPE_SIZE(char);
  6.     BASE_TYPE_SIZE(short);
  7.     BASE_TYPE_SIZE(int);
  8.     BASE_TYPE_SIZE(long);
  9.     BASE_TYPE_SIZE(long long);
  10.     BASE_TYPE_SIZE(float);
  11.     BASE_TYPE_SIZE(double);
  12.     BASE_TYPE_SIZE(long double);
  13.     BASE_TYPE_SIZE(void*);
  14.     BASE_TYPE_SIZE(char*);
  15.     BASE_TYPE_SIZE(int*);
  16.    
  17.     typedef struct
  18.     {
  19.     }StructNull;
  20.     BASE_TYPE_SIZE(StructNull);
  21.     BASE_TYPE_SIZE(StructNull*);
  22. }
复制代码

结果是:

  1.         void :  1 Byte
  2.         char :  1 Byte
  3.        short :  2 Bytes
  4.          int :  4 Bytes
  5.         long :  4 Bytes
  6.    long long :  8 Bytes
  7.        float :  4 Bytes
  8.       double :  8 Bytes
  9. long double : 12 Bytes
  10.        void* :  4 Bytes
  11.        char* :  4 Bytes
  12.         int* :  4 Bytes
  13.   StructNull :  0 Byte
  14. StructNull* :  4 Bytes
复制代码


这些内容不用记住,不同平台是不一样的,使用之前,一定要亲自测试验证下,但是可以总结出以下信息:
  • void类型不是空的,占一个字节
  • long不一定比int大
  • C语言空结构体的大小为0(注意:C++的为1)
  • 不管什么类型,指针都是相同大小的


2. C语言结构体的内存对齐
我先看个例子:

  1. #define offset(type, member)      (size_t)&(((type *)0)->member)
  2. #define STRUCT_E_ADDR(s,e)          printf("%5s size = %2d %16s addr: %p\n", #s, sizeof(s), #s"."#e, &s.e)
  3. #define STRUCT_E_OFFSET(s,e)        printf("%5s size = %2d %16s offset: %2d\n", #s, sizeof(s), #s"."#e, offset(__typeof__(s),e))
  4. #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))

  5. typedef struct
  6. {
  7.     int e_int;
  8.     char e_char;
  9. }S1;
  10. S1 s1;
  11. STRUCT_E_ADDR_OFFSET(s1, e_int);
  12. STRUCT_E_ADDR_OFFSET(s1, e_char);
  13. typedef struct
  14. {
  15.     int e_int;
  16.     double e_double;
  17. }S11;
  18. S11 s11;
  19. STRUCT_E_ADDR_OFFSET(s11, e_int);
  20. STRUCT_E_ADDR_OFFSET(s11, e_double);
复制代码


咦……这宏定义是啥意思?传送门:《基于C99规范,最全C语言预处理知识总结输出结果:

  1.    s1 size =  8         s1.e_int addr: 0028FF28, offset:  0
  2.    s1 size =  8        s1.e_char addr: 0028FF2C, offset:  4
  3.   s11 size = 16        s11.e_int addr: 0028FF18, offset:  0
  4.   s11 size = 16     s11.e_double addr: 0028FF20, offset:  8
复制代码

结论1:一般情况下,结构体所占的内存大小并非元素本身大小之和。

结论2:不严谨地,结构体内存的大小按最大元素大小对齐。
继续看例子:

  1.     typedef struct
  2.     {
  3.         int e_int;
  4.         long double e_ld;
  5.     }S12;

  6.     typedef struct
  7.     {
  8.         long long e_ll;
  9.         long double e_ld;
  10.     }S13;

  11.     typedef struct
  12.     {
  13.         char e_char;
  14.         long double e_ld;
  15.     }S14;

  16.     S12 s12;
  17.     S13 s13;
  18.     S14 s14;
  19.     STRUCT_E_ADDR_OFFSET(s12, e_int);
  20.     STRUCT_E_ADDR_OFFSET(s12, e_ld);
  21.     STRUCT_E_ADDR_OFFSET(s13, e_ll);
  22.     STRUCT_E_ADDR_OFFSET(s13, e_ld);
  23.     STRUCT_E_ADDR_OFFSET(s14, e_char);
  24.     STRUCT_E_ADDR_OFFSET(s14, e_ld);
复制代码

输出结果:

  1.   s12 size = 16        s12.e_int addr: 0028FF08, offset:  0
  2.   s12 size = 16         s12.e_ld addr: 0028FF0C, offset:  4
  3.   s13 size = 24         s13.e_ll addr: 0028FEF0, offset:  0
  4.   s13 size = 24         s13.e_ld addr: 0028FEF8, offset:  8
  5.   s14 size = 16       s14.e_char addr: 0028FEE0, offset:  0
  6.   s14 size = 16         s14.e_ld addr: 0028FEE4, offset:  4
复制代码


出现问题了,你看s12和s14,sizeof(long long)应该是12,按结论而推断sizeof(s12)和sizeof(s13)应该都是24。
这里跟平台和编译器的一个模数有关。
对结论2修正:结构体内存大小应按最大元素大小对齐,如果最大元素大小超过模数,应按模数大小对齐。
额外再送一条结论:如果结构体的最大元素大小超过模数,结构体的起始地址是可以被模数整除的。如果,最大元素大小没有超过模数大小,那它的起始地址是可以被最大元素大小整除
那么,这个模数是什么?
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。
网上流传一个表:
平台
长度/模数
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.     typedef struct
  2.     {
  3.         int e_int;
  4.         double e_double;
  5.     }S11;
  6.     S11 s11;
  7.     STRUCT_E_ADDR_OFFSET(s11, e_int);
  8.     STRUCT_E_ADDR_OFFSET(s11, e_double);
复制代码


结果是:

  1.   s11 size = 16        s11.e_int addr: 0028FF18, offset:  0
  2.   s11 size = 16     s11.e_double addr: 0028FF20, offset:  8
复制代码


很明显,上表没有一项完全对应得上的。简单汇总以下我测试的结果:
长度/模数
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就是你要指定的“对齐系数”。例如

  1. #pragma pack(1)
  2. typedef struct
  3. {
  4.     char e_char;
  5.     long double e_ld;
  6. }S14;
  7. #pragma pack()
复制代码


#pragma是啥玩意?有兴趣可以看看:《基于C99规范,最全C语言预处理知识总结
好了,我们继续,这似乎没啥技术含量,我们提升下难度:

  1.     typedef struct
  2.     {
  3.         int e_int;
  4.         char e_char1;
  5.         char e_char2;
  6.     }S2;

  7.     typedef struct
  8.     {
  9.         char e_char1;
  10.         int e_int;
  11.         char e_char2;
  12.     }S3;
  13.     S2 s2;
  14.     S3 s3;
复制代码


你觉得这俩结构体所占内存是一样大吗?那你就错了:

  1.    s2 size =  8         s2.e_int addr: 0028FED4, offset:  0
  2.    s2 size =  8       s2.e_char1 addr: 0028FED8, offset:  4
  3.    s2 size =  8       s2.e_char2 addr: 0028FED9, offset:  5
  4.    s3 size = 12       s3.e_char1 addr: 0028FEC4, offset:  0
  5.    s3 size = 12         s3.e_int addr: 0028FEC8, offset:  4
  6.    s3 size = 12       s3.e_char2 addr: 0028FECC, offset:  8
复制代码


why?
上个图先看看,它们内存是怎么对齐的:
微信图片_20200617125959.png
我们套一遍那几条结论就可以知道:
理解按最大元素大小或模数对齐,就可以看到S2的内存分布;
对于S3,e_int的位置地址,肯定是要按int的大小对齐的(地址可被int大小整除),这样才能提高访问效率。同时,这导致了很大的内存浪费。
以上例子,我们看到挨在一起的两个char会放在同一个对齐单元,如果挨在一起的short和char会不会放一起?

  1.     typedef struct
  2.     {
  3.         char e_char1;
  4.         short e_short;
  5.         char e_char2;
  6.         int e_int;
  7.         char e_char3;
  8.     }S4;
  9.     S4 s4;
  10.     STRUCT_E_ADDR_OFFSET(s4, e_char1);
  11.     STRUCT_E_ADDR_OFFSET(s4, e_short);
  12.     STRUCT_E_ADDR_OFFSET(s4, e_char2);
  13.     STRUCT_E_ADDR_OFFSET(s4, e_int);
  14.     STRUCT_E_ADDR_OFFSET(s4, e_char3);
复制代码


输出结果:

  1.    s4 size = 16       s4.e_char1 addr: 0028FEB4, offset:  0
  2.    s4 size = 16       s4.e_short addr: 0028FEB6, offset:  2
  3.    s4 size = 16       s4.e_char2 addr: 0028FEB8, offset:  4
  4.    s4 size = 16         s4.e_int addr: 0028FEBC, offset:  8
  5.    s4 size = 16       s4.e_char3 addr: 0028FEC0, offset: 12
复制代码


微信图片_20200617130003.png
得出一个经验:
我们在定义结构体的时候,尽量把大小相同或相近的元素放一起,以减少结构体占用的内存空间。
再来一个问题:
结构体套着另一个结构体怎么计算?

  1.     typedef struct
  2.     {
  3.         int e_int;
  4.         char e_char;
  5.     }S1;
  6.    typedef struct
  7.     {
  8.         S1 e_s;
  9.         char e_char;
  10.     }SS1;

  11.     typedef struct
  12.     {
  13.         short e_short;
  14.         char e_char;
  15.     }S6;

  16.     typedef struct
  17.     {
  18.         S6 e_s;
  19.         char e_char;
  20.     }SS2;
  21.    
  22.     SS1 ss1;
  23.     STRUCT_E_ADDR_OFFSET(ss1, e_s);
  24.     STRUCT_E_ADDR_OFFSET(ss1, e_char);

  25.     SS2 ss2;
  26.     STRUCT_E_ADDR_OFFSET(ss2, e_s);
  27.     STRUCT_E_ADDR_OFFSET(ss2, e_char);
复制代码


输出结果:

  1.   ss1 size = 12          ss1.e_s addr: 0028FE94, offset:  0
  2.   ss1 size = 12       ss1.e_char addr: 0028FE9C, offset:  8
  3.   ss2 size =  6          ss2.e_s addr: 0028FE8E, offset:  0
  4.   ss2 size =  6       ss2.e_char addr: 0028FE92, offset:  4
复制代码


微信图片_20200617130006.png
得出结论:结构体内的结构体,结构体内的元素并不会和结构体外的元素合并占一个对齐单元。
温馨提示:大家不要刻意去记这些结论,动手去试试并思考下效果会更好。
3. 联合体union的内存对齐
直接上代码:

  1.     typedef union
  2.     {
  3.         char e_char;
  4.         int e_int;
  5.     }U1;

  6.     U1 u1;
  7.     STRUCT_E_ADDR(u1, e_char);
  8.     STRUCT_E_ADDR(u1, e_int);
复制代码


输出结果:

  1.    u1 size =  4        u1.e_char addr: 0028FF2C
  2.    u1 size =  4         u1.e_int addr: 0028FF2C
复制代码


从教科书上,我都可以理解,联合体里面的元素,实际上共享同一个空间。
微信图片_20200617130009.png
那么,union跟struct结合呢?

  1.     typedef struct
  2.     {
  3.         int e_int1;
  4.         union
  5.         {
  6.             char ue_chars[9];
  7.             int ue_int;
  8.         }u;
  9.         double e_double;
  10.         int e_int2;
  11.     }SU2;
  12.     SU2 su2;
  13.     STRUCT_E_ADDR_OFFSET(su2, e_int1);
  14.     STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);
  15.     STRUCT_E_ADDR_OFFSET(su2, u.ue_int);
  16.     STRUCT_E_ADDR_OFFSET(su2, e_double);
  17.     STRUCT_E_ADDR_OFFSET(su2, e_int2)
复制代码

输出:

  1.   su2 size = 32       su2.e_int1 addr: 0028FEF8, offset:  0
  2.   su2 size = 32   su2.u.ue_chars addr: 0028FEFC, offset:  4
  3.   su2 size = 32     su2.u.ue_int addr: 0028FEFC, offset:  4
  4.   su2 size = 32     su2.e_double addr: 0028FF08, offset: 16
  5.   su2 size = 32       su2.e_int2 addr: 0028FF10, offset: 24
复制代码

微信图片_20200617130012.png
实际上跟结构体类似,也没有特别的规则。
顺便提一下,使用union时,要留意平台的大小端问题。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
百度百科——大小端模式
怎么获知自己使用的平台的大小端?Linux有个方法:

  1.     static union {
  2.         char c[4];
  3.         unsigned long l;
  4.     } endian_test = { { 'l', '?', '?', 'b' } };
  5.     #define ENDIANNESS ((char)endian_test.l)

  6.     printf("ENDIANNESS: %c\n", ENDIANNESS);
复制代码


4. 位域(Bitfield)的相关
位域在本文没什么好探讨的,在结构体对齐方面没什么特别的地方。
直接看个测试代码,就可以明白:

  1. void bitfield_type_size(void)
  2. {
  3.     typedef struct
  4.     {
  5.         char bf1:1;
  6.         char bf2:1;
  7.         char bf3:1;
  8.         char bf4:3;
  9.     }SB1;

  10.     typedef struct
  11.     {
  12.         char bf1:1;
  13.         char bf2:1;
  14.         char bf3:1;
  15.         char bf4:7;
  16.     }SB2;

  17.     typedef struct
  18.     {
  19.         char bf1:1;
  20.         char bf2:1;
  21.         char bf3:1;
  22.         int  bfint:1;
  23.     }SB3;

  24.     typedef struct
  25.     {
  26.         char bf1:1;
  27.         char bf2:1;
  28.         int  bfint:1;
  29.         char bf3:1;
  30.     }SB4;

  31.     SB1 sb1;
  32.     SB2 sb2;
  33.     SB3 sb3;
  34.     SB4 sb4;
  35.     VAR_ADDR(sb1);
  36.     VAR_ADDR(sb2);
  37.     VAR_ADDR(sb3);
  38.     VAR_ADDR(sb4);
  39.    
  40.     typedef struct
  41.     {
  42.         unsigned char bf1:1;
  43.         unsigned char bf2:1;
  44.         unsigned char bf3:1;
  45.         unsigned char bf4:3;
  46.     }SB11;

  47.     typedef union
  48.     {
  49.         SB11 sb1;
  50.         unsigned char  e_char;
  51.     }UB1;
  52.     UB1 ub1;

  53.     STRUCT_E_ADDR_OFFSET(ub1, sb1);
  54.     STRUCT_E_ADDR_OFFSET(ub1, e_char);

  55.     ub1.e_char = 0xF5;
  56.     BITFIELD_VAL(ub1, e_char);
  57.     BITFIELD_VAL(ub1, sb1.bf1);
  58.     BITFIELD_VAL(ub1, sb1.bf2);
  59.     BITFIELD_VAL(ub1, sb1.bf3);
  60.     BITFIELD_VAL(ub1, sb1.bf4);
  61. }
复制代码

输出结果是:

  1.   sb1 size =  1        sb1 addr: 0028FF2F
  2.   sb2 size =  2        sb2 addr: 0028FF2D
  3.   sb3 size =  8        sb3 addr: 0028FF24
  4.   sb4 size = 12        sb4 addr: 0028FF18
  5.   ub1 size =  1          ub1.sb1 addr: 0028FF17, offset:  0
  6.   ub1 size =  1       ub1.e_char addr: 0028FF17, offset:  0
  7.          ub1 :  1 Byte, ub1.e_char=0xF5
  8.          ub1 :  1 Byte, ub1.sb1.bf1=0x1
  9.          ub1 :  1 Byte, ub1.sb1.bf2=0x0
  10.          ub1 :  1 Byte, ub1.sb1.bf3=0x1
  11.          ub1 :  1 Byte, ub1.sb1.bf4=0x6
复制代码

有几个点需要注意下:
  • 内存的计算单位是byte,不是bit
  • 结构体内即使有bitfield元素,其对齐规则还是按照基本类型来
  • bitfield元素不能获得其地址(即程序中不能通过&取址)


5. 规则总结首先,不推荐记忆这些条条框框的文字,以下内容仅供参考:
  • 结构体的内存大小,并非其内部元素大小之和;
  • 结构体变量的起始地址,可以被最大元素基本类型大小或者模数整除;
  • 结构体的内存对齐,按照其内部最大元素基本类型或者模数大小对齐;
  • 模数在不同平台值不一样,也可通过#pragma pack(n)方式去改变;
  • 如果空间地址允许,结构体内部元素会拼凑一起放在同一个对齐空间;
  • 结构体内有结构体变量元素,其结构体并非展开后再对齐;
  • union和bitfield变量也遵循结构体内存对齐原则。


▍编程为什么要关注结构体内存对齐也许你会问,结构体爱怎么对齐就怎么对齐,我管它干嘛!
1. 节省内存在嵌入式软件开发中,特别是内存资源匮乏的小MCU,这个尤为重要。如果优化程序内存,使得MCU可以选更小的型号,对于大批量出货的产品,可以带来更高利润。也许你还还感觉不到,上段代码:

  1.     typedef struct
  2.     {
  3.         int e_int;
  4.         char e_char1;
  5.         char e_char2;
  6.     }S2;

  7.     typedef struct
  8.     {
  9.         char e_char1;
  10.         int e_int;
  11.         char e_char2;
  12.     }S3;
  13.    
  14.     S2 s2[1024] = {0};
  15.     S3 s3[1024] = {0};
复制代码


s2的大小为8K,而s3的大小为12K,一放大,就有很明显的区别了。
2. union的内存对齐需要对于同一个内存,有时为了满足不同的访问形式,定义一个联合体变量,或者一个结构体和联合体组合的变量。此时就要知道其内存结构是怎么分布的。
3. 内存拷贝
有时候,我们在通信数据接收处理时候,往往遇到,数组和结构体的搭配。
即,通信时候,通常使用数组参数形式接收,而处理的时候,按照预定义格式去访问处理。例如:

  1. U8 comm_data[10];
  2. typedef struct
  3. {
  4.     U8 id;
  5.     U16 len;
  6.     U8 data[6];
  7. }FRAME;

  8. FRAME* pFram = (FRAME*)comm_data;
复制代码

此处,必须要理解这个FRAM的内存结构是怎么样的对齐规则。
4. 调试仿真时看压栈数据在调试某些奇葩问题时,迫不得已,我们会研究函数跳转或者线程切换时的栈数据,遇到结构体内容,肯定要懂得其内存对齐方式才能更好地获得栈内信息。
当然,还有其他方面的原因,在此就不一一列举了。

▍结构体内存对齐实际应用
上面一个章节已经部分讲到这个结构体内存对齐的应用了,例如通信数据的处理等。另外,再举两个例子:1. 内存的mapping假设你要做一个烧录文件,你想往文件头空间128个字节内放一段项目信息(例如程序大小、CRC校验码、其他项目信息等)。第一反应,你会考虑用一个结构体,定义一段这样的数据,程序运行的时候也定义同样的结构体去读取这个内存。但是你需要知道结构体大小啊,这个结构体内存对齐的规则还是需要了解的。2. 单片机寄存器的mapping在写MCU驱动的时候,访问寄存器的方式有很多种,但是做到清晰明了,适配性好的,往往需要诸多考量。
直接通过整型指针指到特定地址去访问,是没有问题的,但是对于某一类型的寄存器,往往不是一个固定地址,其后面还有一堆子寄存器属性需要配置。每个地址都通过整型指针访问,那就很多很凌乱。我们可以通过定义一个特定的结构体,用其指针直接mapping到寄存器的base地址。但是遇到有些地址是空的怎么办?甚至有些寄存器是32位的,有些16位,甚至8位的,各种参差不齐都在里面。那就要考虑结构体内存对齐了,特别是结构体内有不同类型的元素。
微信图片_20200617130015.png
这里只探讨应用场景,具体实现还要根据实际情况来定义。


▍测试源码

  1. #include <stdio.h>

  2. #define offset(type, member)      (size_t)&(((type *)0)->member)

  3. #define STRUCT_E_ADDR(s,e)          printf("%5s size = %2d %16s addr: %p\n", #s, sizeof(s), #s"."#e, &s.e)
  4. #define STRUCT_E_OFFSET(s,e)        printf("%5s size = %2d %16s offset: %2d\n", #s, sizeof(s), #s"."#e, offset(__typeof__(s),e))
  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))
  6. #define VAR_ADDR(v)                 printf("%5s size = %2d %10s addr: %p\n", #v, sizeof(v), #v, &v)
  7. // #define BASE_TYPE_SIZE(t)   printf("%18s = %d\n", "sizeof("#t")", sizeof(t))
  8. #define BASE_TYPE_SIZE(t)           printf("%12s : %2d Byte%s\n", #t, sizeof(t), (sizeof(t))>1?"s":"")
  9. #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)

  10. void base_type_size(void)
  11. {
  12.     BASE_TYPE_SIZE(void);
  13.     BASE_TYPE_SIZE(char);
  14.     BASE_TYPE_SIZE(short);
  15.     BASE_TYPE_SIZE(int);
  16.     BASE_TYPE_SIZE(long);
  17.     BASE_TYPE_SIZE(long long);
  18.     BASE_TYPE_SIZE(float);
  19.     BASE_TYPE_SIZE(double);
  20.     BASE_TYPE_SIZE(long double);
  21.     BASE_TYPE_SIZE(void*);
  22.     BASE_TYPE_SIZE(char*);
  23.     BASE_TYPE_SIZE(int*);
  24. }
  25. void struct_type_size(void)
  26. {
  27.     typedef struct
  28.     {
  29.     }StructNull;

  30.     typedef struct
  31.     {
  32.         int e_int;
  33.         char e_char;
  34.     }S1;
  35.     BASE_TYPE_SIZE(StructNull);
  36.     BASE_TYPE_SIZE(StructNull*);
  37.    
  38.     S1 s1;
  39.     STRUCT_E_ADDR_OFFSET(s1, e_int);
  40.     STRUCT_E_ADDR_OFFSET(s1, e_char);

  41.     typedef struct
  42.     {
  43.         int e_int;
  44.         double e_double;
  45.     }S11;

  46.     typedef struct
  47.     {
  48.         int e_int;
  49.         long double e_ld;
  50.     }S12;

  51.     typedef struct
  52.     {
  53.         long long e_ll;
  54.         long double e_ld;
  55.     }S13;

  56.     typedef struct
  57.     {
  58.         char e_char;
  59.         long double e_ld;
  60.     }S14;

  61.     S11 s11;
  62.     S12 s12;
  63.     S13 s13;
  64.     S14 s14;
  65.     STRUCT_E_ADDR_OFFSET(s11, e_int);
  66.     STRUCT_E_ADDR_OFFSET(s11, e_double);
  67.     STRUCT_E_ADDR_OFFSET(s12, e_int);
  68.     STRUCT_E_ADDR_OFFSET(s12, e_ld);
  69.     STRUCT_E_ADDR_OFFSET(s13, e_ll);
  70.     STRUCT_E_ADDR_OFFSET(s13, e_ld);
  71.     STRUCT_E_ADDR_OFFSET(s14, e_char);
  72.     STRUCT_E_ADDR_OFFSET(s14, e_ld);

  73.     typedef struct
  74.     {
  75.         int e_int;
  76.         char e_char1;
  77.         char e_char2;
  78.     }S2;

  79.     typedef struct
  80.     {
  81.         char e_char1;
  82.         int e_int;
  83.         char e_char2;
  84.     }S3;
  85.     typedef struct
  86.     {
  87.         char e_char1;
  88.         short e_short;
  89.         char e_char2;
  90.         int e_int;
  91.         char e_char3;
  92.     }S4;
  93.     typedef struct
  94.     {
  95.         long long e_ll;
  96.         int e_int;
  97.     }S5;

  98.     typedef struct
  99.     {
  100.         S1 e_s;
  101.         char e_char;
  102.     }SS1;

  103.     typedef struct
  104.     {
  105.         short e_short;
  106.         char e_char;
  107.     }S6;

  108.     typedef struct
  109.     {
  110.         S6 e_s;
  111.         char e_char;
  112.     }SS2;


  113.     char var1;
  114.     S2 s2;
  115.     char var2;
  116.     S3 s3;
  117.     VAR_ADDR(var1);
  118.     STRUCT_E_ADDR_OFFSET(s2, e_int);
  119.     STRUCT_E_ADDR_OFFSET(s2, e_char1);
  120.     STRUCT_E_ADDR_OFFSET(s2, e_char2);
  121.     VAR_ADDR(var2);
  122.     STRUCT_E_ADDR_OFFSET(s3, e_char1);
  123.     STRUCT_E_ADDR_OFFSET(s3, e_int);
  124.     STRUCT_E_ADDR_OFFSET(s3, e_char2);
  125.     S4 s4;
  126.     STRUCT_E_ADDR_OFFSET(s4, e_char1);
  127.     STRUCT_E_ADDR_OFFSET(s4, e_short);
  128.     STRUCT_E_ADDR_OFFSET(s4, e_char2);
  129.     STRUCT_E_ADDR_OFFSET(s4, e_int);
  130.     STRUCT_E_ADDR_OFFSET(s4, e_char3);
  131.     S5 s5;
  132.     STRUCT_E_ADDR_OFFSET(s5, e_ll);
  133.     STRUCT_E_ADDR_OFFSET(s5, e_int);
  134.     SS1 ss1;
  135.     STRUCT_E_ADDR_OFFSET(ss1, e_s);
  136.     STRUCT_E_ADDR_OFFSET(ss1, e_char);

  137.     SS2 ss2;
  138.     STRUCT_E_ADDR_OFFSET(ss2, e_s);
  139.     STRUCT_E_ADDR_OFFSET(ss2, e_char);
  140. }

  141. void union_type_size(void)
  142. {
  143.     typedef union
  144.     {
  145.         char e_char;
  146.         int e_int;
  147.     }U1;

  148.     U1 u1;
  149.     STRUCT_E_ADDR_OFFSET(u1, e_char);
  150.     STRUCT_E_ADDR_OFFSET(u1, e_int);
  151.    

  152.     typedef struct
  153.     {
  154.         short e_short;
  155.         union
  156.         {
  157.             char ue_chars[9];
  158.             int ue_int;
  159.         }u;
  160.     }SU1;

  161.     typedef struct
  162.     {
  163.         int e_int1;
  164.         union
  165.         {
  166.             char ue_chars[9];
  167.             int ue_int;
  168.         }u;
  169.         double e_double;
  170.         int e_int2;
  171.     }SU2;

  172.     SU1 su1;
  173.     SU2 su2;
  174.    
  175.     STRUCT_E_ADDR_OFFSET(su1, e_short);
  176.     STRUCT_E_ADDR_OFFSET(su1, u.ue_chars);
  177.     STRUCT_E_ADDR_OFFSET(su1, u.ue_int);
  178.    
  179.     STRUCT_E_ADDR_OFFSET(su2, e_int1);
  180.     STRUCT_E_ADDR_OFFSET(su2, u.ue_chars);
  181.     STRUCT_E_ADDR_OFFSET(su2, u.ue_int);
  182.     STRUCT_E_ADDR_OFFSET(su2, e_double);
  183.     STRUCT_E_ADDR_OFFSET(su2, e_int2);
  184. }

  185. void bitfield_type_size(void)
  186. {
  187.     typedef struct
  188.     {
  189.         char bf1:1;
  190.         char bf2:1;
  191.         char bf3:1;
  192.         char bf4:3;
  193.     }SB1;

  194.     typedef struct
  195.     {
  196.         char bf1:1;
  197.         char bf2:1;
  198.         char bf3:1;
  199.         char bf4:7;
  200.     }SB2;

  201.     typedef struct
  202.     {
  203.         char bf1:1;
  204.         char bf2:1;
  205.         char bf3:1;
  206.         int  bfint:1;
  207.     }SB3;

  208.     typedef struct
  209.     {
  210.         char bf1:1;
  211.         char bf2:1;
  212.         int  bfint:1;
  213.         char bf3:1;
  214.     }SB4;

  215.     SB1 sb1;
  216.     SB2 sb2;
  217.     SB3 sb3;
  218.     SB4 sb4;
  219.     // STRUCT_E_OFFSET(sb1, bf1);
  220.     // STRUCT_E_OFFSET(sb1, bf2);
  221.     // STRUCT_E_OFFSET(sb1, bf3);
  222.     // STRUCT_E_OFFSET(sb1, bf4);
  223.     VAR_ADDR(sb1);
  224.     VAR_ADDR(sb2);
  225.     VAR_ADDR(sb3);
  226.     VAR_ADDR(sb4);
  227.    
  228.     typedef struct
  229.     {
  230.         unsigned char bf1:1;
  231.         unsigned char bf2:1;
  232.         unsigned char bf3:1;
  233.         unsigned char bf4:3;
  234.     }SB11;

  235.     typedef union
  236.     {
  237.         SB11 sb1;
  238.         unsigned char  e_char;
  239.     }UB1;
  240.     UB1 ub1;

  241.     STRUCT_E_ADDR_OFFSET(ub1, sb1);
  242.     STRUCT_E_ADDR_OFFSET(ub1, e_char);

  243.     ub1.e_char = 0xF5;
  244.     BITFIELD_VAL(ub1, e_char);
  245.     BITFIELD_VAL(ub1, sb1.bf1);
  246.     BITFIELD_VAL(ub1, sb1.bf2);
  247.     BITFIELD_VAL(ub1, sb1.bf3);
  248.     BITFIELD_VAL(ub1, sb1.bf4);

  249.     static union {
  250.         char c[4];
  251.         unsigned long l;
  252.     } endian_test = { { 'l', '?', '?', 'b' } };
  253.     #define ENDIANNESS ((char)endian_test.l)

  254.     printf("ENDIANNESS: %c\n", ENDIANNESS);

  255. }
  256. int main(void)
  257. {
  258.     // base_type_size();
  259.     struct_type_size();
  260.     union_type_size();
  261.     bitfield_type_size();

  262.     return 0;
  263. }
复制代码


收藏 评论0 发布时间:2020-6-17 13:01

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版