C语言结构体字节对齐问题

本文探讨了C语言中结构体字节对齐的问题,澄清了一个关于字节对齐的常见误区。通过实例分析,解释了字节对齐的真正原因是为了提高CPU数据访问效率,并详细解析了AD、A8、A16结构体的内存布局。同时,文章还提出了C++11中alignas关键字使用时的疑问,讨论了结构体对齐规则及其与C标准的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C/C++结构体字节对齐问题

1 前言

对于结构体字节对齐问题,我发现我一直有一个误区,正是这个误区,给我带来了很大困扰,今天突然顿悟,在此记录一下,希望能帮到大家。

2 举例

#include <stdio.h>
#define field_offset(s,f) (int)(&(((struct s *)0)->f))
struct AD  { int a; char b[13]; double c;};
#pragma pack(push)
#pragma pack(1)
struct A1  { int a; char b[13]; double c;};
#pragma pack(2)
struct A2  { int a; char b[13]; double c;};
#pragma pack(4)
struct A4  { int a; char b[13]; double c;};
#pragma pack(8)
struct A8  { int a; char b[13]; double c;};
#pragma pack(16)
struct A16 { int a; char b[13]; double c;};
#pragma pack(pop)
int main()
{
    printf("AD.a %d\n",field_offset(AD,a));//AD.a 0
    printf("AD.b %d\n",field_offset(AD,b));//AD.b 4
    printf("AD.c %d\n",field_offset(AD,c));//AD.c 24
    printf("AD sizeof %d\n", sizeof(AD));//AD sizeof 32
    printf("\n");
    printf("A1.a %d\n",field_offset(A1,a));//A1.a 0
    printf("A1.b %d\n",field_offset(A1,b));//A1.b 4
    printf("A1.c %d\n",field_offset(A1,c));//A1.c 17
    printf("A1 sizeof %d\n", sizeof(A1));//A1 sizeof 25
    printf("\n");
    printf("A2.a %d\n",field_offset(A2,a));//A2.a 0
    printf("A2.b %d\n",field_offset(A2,b));//A2.b 4
    printf("A2.c %d\n",field_offset(A2,c));//A2.c 18
    printf("A2 sizeof %d\n", sizeof(A2));//A2 sizeof 26
    printf("\n");
    printf("A4.a %d\n",field_offset(A4,a));//A4.a 0
    printf("A4.b %d\n",field_offset(A4,b));//A4.b 4
    printf("A4.c %d\n",field_offset(A4,c));//A4.c 20
    printf("A4 sizeof %d\n", sizeof(A4));//A4 sizeof 28
    printf("\n");
    printf("A8.a %d\n",field_offset(A8,a));//A8.a 0
    printf("A8.b %d\n",field_offset(A8,b));//A8.b 4
    printf("A8.c %d\n",field_offset(A8,c));//A8.c 24
    printf("A8 sizeof %d\n", sizeof(A8));//A8 sizeof 32
    printf("\n");
    printf("A16.a %d\n",field_offset(A16,a));//A16.a 0
    printf("A16.b %d\n",field_offset(A16,b));//A16.b 4
    printf("A16.c %d\n",field_offset(A16,c));//A16.c 24
    printf("A16 sizeof %d\n", sizeof(A16));//A16 sizeof 32
    printf("\n");
    return 0;
}

如果你认为的结果和上面注释中的一样,那你就没有必要读下去了。

在上面的例子中,对于A1,A2, A4的结果我都理解,但是对AD,A8,A16我很不理解。
对于AD,我认为结果应该是:

AD.a 0
AD.b 8
AD.c 24
AD sizeof 32

我是这样想的:AD应该是8字节对齐的,a是4字节,b占13字节,a后面虽然空了4个字节,但是放不下b,所以应该是a后面补4个字节,接着放b。b13个字节,不是8的倍数,所以b后面补3个字节。接着c占8个字节。

当然,我上面的想法是错误的。可是为什么会错呢?

其实,问题出在b的身上。b是一个char型的数组,即类型为char *,但是我们访问的是b的元素,即char,所以,b的自身对齐值为1,而不是13

之所以这里出错,实际上是因为没有深刻认识到为什么要字节对齐。字节对齐是为了提高CPU访问数据的效率。32位字长的计算机,其CPU一次可以读写的数据长度是4个字节64位计算机则一次可以读取8个字节。对于char型数组,只占据一个字节,小于计算机一次读取的字节数,所以无论其元素放在什么地址,都可以一次读取到,便“不需要”字节对齐。所以,b的元素是可以紧挨着a来放的,所以AD实际空间存放情况应如下图所示:

在这里插入图片描述

AD成员中自身对齐值最大的为8, 所以AD的自身对齐值是8;A8的指定对齐值和自身对齐值一样,相当于没指定,还是8字节对齐;A16指定对齐值16大于自身对齐值8,所以还是8字节对齐。所以AD,A8,A16的结果是一样的。

3.其他

这是一个C++程序:

#define field_offset(s,f) (int)(&(((struct s *)0)->f))
int main() {
    struct alignas(4) Info {
        char a;
        short b;
        char c;
    };
    std::cout << sizeof(Info) << std::endl;
    std::cout << field_offset(Info, a) << std::endl;
    std::cout << field_offset(Info, b) << std::endl;
    std::cout << field_offset(Info, c) << std::endl;

    return 0;
}

刚开始我以为应该是下面这样的:
在这里插入图片描述
所以sizeof(Info) = 4

但运行结果为:

8
0
2
4

可以推断出,Info的实际存储情况应该为:
在这里插入图片描述
针对此,我有2个问题:

  1. C++11标准中,alignas不是最小设置8字节对齐,也就最小是alignas(8),否则设置无效吗?为什么还会出现上面的结果呢?
  2. 如果alignas(4)设置生效,根据第二节的例子中提到的:结构体的有效对齐值是自身对齐值和指定对齐值中较小者。那么实际存储情况像下面这样(sizeof(Info)= 6)啊:
    在这里插入图片描述
    是因为C和C++标准不一样吗?

后续:经过验证,我得到了答案


参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值