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

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个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

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