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

C/C++ 专栏收录该内容
18 篇文章 2 订阅

C语言结构体对齐规则

不同的编译器和系统默认的对齐规则会有差异,这里我使用的32bit的MinGW。

1、结构对齐

对齐规则说明:

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

示例1:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define debug_printf(value) printf(#value " ---==> %d\n", value)

#define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))

typedef struct
{
	uint8_t  a;
	uint32_t b;
	uint8_t  c;
	uint8_t  d;
}test_t;

int main()
{
	int a = struct_member_offset(test_t, a);
	int b = struct_member_offset(test_t, b);
	int c = struct_member_offset(test_t, c);
	int d = struct_member_offset(test_t, d);
	debug_printf(sizeof(test_t));
	debug_printf(a);
	debug_printf(b);
	debug_printf(c);
	debug_printf(d);
	return 0;
}

输出结果:
在这里插入图片描述
解析:

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

示例二:

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

typedef struct
{
	uint8_t  a;
	uint32_t b;
	uint8_t  c;
	uint64_t  d;
}test_t;

输出结果:

还是根据规则解析:

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

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

typedef struct
{
	uint8_t  a;
	uint32_t b;
	uint8_t c[3];
	uint8_t  d;
	uint64_t  e;
}test_t;

输出结果:
在这里插入图片描述
c成员为3字节,占用偏移地址8,9,10三个空间,所以总大小还是24。


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

typedef struct
{
	uint8_t sub_a;
	uint32_t sub_b;
}sub_t;

typedef struct
{
	uint8_t  a;
	uint32_t b;
	sub_t c;
	uint8_t  d;
	uint64_t  e;
}test_t;

输出结果:
在这里插入图片描述

解析:

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

2、字节为0的数组与结构体

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

int main()
{
	int array[0];
	debug_printf(sizeof(array));
	return 0;
}

输出结果:

sizeof(array) ---==> 0

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

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define debug_printf(value) printf(#value " ---==> %d\n", value)

#define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))

typedef struct
{
	uint8_t  a;
	uint32_t b;
	uint8_t  c;
	uint64_t  d[0];
}test_t;

int main()
{
	int a = struct_member_offset(test_t, a);
	int b = struct_member_offset(test_t, b);
	int c = struct_member_offset(test_t, c);
	int d = struct_member_offset(test_t, d);
	debug_printf(sizeof(test_t));
	debug_printf(a);
	debug_printf(b);
	debug_printf(c);
	debug_printf(d);
	return 0;
}

输出结果:
在这里插入图片描述
!!奇怪的问题出现了!!

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

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


ends…

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页

打赏

y-zheng

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值