C语言基础:结构体对齐规则与0字节数组

梦里梦外; 2023-03-04 09:16 61阅读 0赞

C语言基础:结构体对齐规则与0字节数组


  • 不同的编译器和系统默认的对齐规则会有差异,这里我使用的win32的MinGW
  • C语言结构体一般是默认四字节对其的。

结构体对其规则

一般的,C语言结构体默认是以4字节对其方式,以此默认4字节为依据,结构体对其规则有以下三项:

  • 规则一:struct内的第一个成员在偏移地址0处,随后成员的偏移地址在其本身类型大小整数倍处
  • 规则二:struct的总大小为内部最大成员类型的整数倍
  • 规则三:当A结构内含有结构B时,B在A中的偏移地址为B结构内最大元素类型的大小的整数倍

示例1:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #define debug_printf(value) printf(#value " ---==> %d\n", value)
  5. #define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))
  6. typedef struct
  7. {
  8. uint8_t a; // 偏移地址0
  9. uint32_t b; // 偏移地址4
  10. uint8_t c; // 偏移地址8
  11. uint8_t d; // 偏移地址9
  12. }test_t;
  13. int main()
  14. {
  15. int a = struct_member_offset(test_t, a);
  16. int b = struct_member_offset(test_t, b);
  17. int c = struct_member_offset(test_t, c);
  18. int d = struct_member_offset(test_t, d);
  19. debug_printf(sizeof(test_t));
  20. debug_printf(a);
  21. debug_printf(b);
  22. debug_printf(c);
  23. debug_printf(d);
  24. return 0;
  25. }

输出结果:
20200803090449600.png
解析:

  • a为第一个成员在偏移地址0处
  • b成员类型为无符号整形占用四字节,根据规则一b要对齐到4字节地址处,所以偏移地址为4,相当于在a成员后面补齐了3字节,不过这补齐的3个字节没用到
  • c和d成员大小都为1字节,根据规则一要对齐到1字节,所以c偏移为9,d偏移为10
  • 四个成员目前所占用的空间加起来是10字节,但是!此时还没完!根据规则二结构体的总大小为结构内最大成员的整数倍,test_t这个结构内最大成员是b占用4字节,所以10字节还要补齐2字节去对齐4字节,所以sizeof(test_t)=12。

示例二:

现在将d成员改为uint64类型:

  1. typedef struct
  2. {
  3. uint8_t a;
  4. uint32_t b;
  5. uint8_t c;
  6. uint64_t d;
  7. }test_t;

输出结果:
dea5d92cb38546d9baf7bf8c25ae0dcb.png

还是根据规则解析:

  • a在偏移地址0处
  • b在4处
  • c在8处
  • abc所占用的空间为9个字节,d为uint64类型占8个字节,所以要对齐到8字节处,也就是偏移地址16处,相当于c成员后补齐了7字节。
  • 目前四个成员所占用的空间为24字节,24是d类型的倍数大小,所以sizeof(test_t)=24

示例三:
现在将结构改为如下,多了一个数据

  1. typedef struct
  2. {
  3. uint8_t a;
  4. uint32_t b;
  5. uint8_t c[3];
  6. uint8_t d;
  7. uint64_t e;
  8. }test_t;

输出结果:
202008030925112.png
c成员为3字节,占用偏移地址8,9,10三个空间,所以总大小还是24。


示例四:
将结构改为如下,现在test_t结构内包含结构sub_t。

  1. typedef struct
  2. {
  3. uint8_t sub_a;
  4. uint32_t sub_b;
  5. }sub_t;
  6. typedef struct
  7. {
  8. uint8_t a;
  9. uint32_t b;
  10. sub_t c;
  11. uint8_t d;
  12. uint64_t e;
  13. }test_t;

输出结果:
20200803093431314.png

解析:

  • 根据规则一和二解析sizeof(sub_t)=8字节
  • 根据规则三确定sub_t在test_t中的偏移地址肯定是4的整数倍处,所以c这个结构成员偏移地址为8
  • d偏移地址为17
  • e偏移地址为24
  • sizeof(test_t)=32

字节为0的数组与结构体

先看一下0字节数组的大小:

  1. int main()
  2. {
  3. int array[0];
  4. debug_printf(sizeof(array));
  5. return 0;
  6. }

输出结果:

  1. sizeof(array) ---==> 0

说明0字节数据占用空间为0,那么看下面这个例子:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #define debug_printf(value) printf(#value " ---==> %d\n", value)
  5. #define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))
  6. typedef struct
  7. {
  8. uint8_t a;
  9. uint32_t b;
  10. uint8_t c;
  11. uint64_t d[0];
  12. }test_t;
  13. int main()
  14. {
  15. int a = struct_member_offset(test_t, a);
  16. int b = struct_member_offset(test_t, b);
  17. int c = struct_member_offset(test_t, c);
  18. int d = struct_member_offset(test_t, d);
  19. debug_printf(sizeof(test_t));
  20. debug_printf(a);
  21. debug_printf(b);
  22. debug_printf(c);
  23. debug_printf(d);
  24. return 0;
  25. }

输出结果:
20200803113052716.png
!!奇怪的问题出现了!!

但道理说d成员占用空间为0,sizeof(test_t)应该是12才对,但是sizeof(test_t)却是16!!

原因:d成员虽然占用空间为0,但是他是uint64类型的,结构体的总大小是按照内部最大成员进行对齐的!test_t对齐到了8字节,所以sizeof(test_t)的大小为16字节!


ends…

发表评论

表情:
评论列表 (有 0 条评论,61人围观)

还没有评论,来说两句吧...

相关阅读

    相关 结构字节对齐

    /\ 结构体变量占据的内存单元的个数应当大于等于其内部所有的数据成员占据内存单元数据的和 \/ /\ 出于效率的考虑,C语言引入了对齐机制,一般来说,不同的编译器字节对

    相关 C语言结构字节对齐原则

     为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的

    相关 C++结构字节对齐

    前言 在计算机中数据存储和传输以位(bit)为单位,每8个位bit组成1个字节(Byte)。32位计算机的字长为32位,即4个字节;对应的,64位计算机的字长为64位,

    相关 结构字节对齐

    在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访

    相关 C/C++结构字节对齐

    稍微了解过C/C++的人,都会了解它们在建立一个结构体的时候,会进行字节对齐操作,所以往往比世界变量占用的字节数要多出一些,而如何减少内存占用,计算内存使用量,也是很多面试题里