结构体字节对齐详解

结构体字节对齐详解

什么是结构体字节对齐?

假如有个结构体A如下,请问它占用了几个字节?

struct A{
    char a;
    int b;
    short c;
}

以我们的直观感觉,char占用1个字节,int占用4个字节,short占用2个字节,因此总共占用7个字节。

真是这样吗?让我们运行实测一下,测试程序exampl.cpp代码如下所示:

#include <iostream>
using namespace std;
struct A{
    char a;
    int b;
    short c;
};
int main()
{
    A a;
    cout << sizeof(a.a) << ":" <<sizeof(a.b) << ":" << sizeof(a.c) << endl;
    cout << sizeof(a) << endl;
    return 0;
}

编译并执行程序,结果如下所示:
请添加图片描述

每个结构体的占用空间如我们所知道的char是1,int是4,short是2,累加结果应该为7,但最后结构体占用的字节却是12个,这个就是结构体的字节对齐机制

怎么计算结构体占用字节?

结构体内变量的字节对齐需要符合以下规则。

  • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
  • 结构体每个成员相对结构体首地址的偏移量(offset)都是自身有效对齐字节数的整数倍。
  • 结构体内变量的自身有效对齐字节数为自身对齐字节数与系统对齐字节数的较小者;结构体的大小为成员最大自身对齐字节数的倍数。

计算结构体占用字节重点看规则2和规则3,我们先解释几个名词。每个成员的相对于首地址的偏移量(offset)是什么意思,自身对齐字节数是什么意思?系统对齐字节数是什么意思?自身有效对齐字节数是什么意思?最大自身对齐字节数是什么意思?

以如下结构为例进行讲解:

struct A{
    char a;
    int b;
    short c;
}

每个成员的 相对于首地址的偏移量(offset) 是指该变量在结构体中的首地址减去结构体中的首地址。

编译并运行程序example.cpp

g++ -g example.cpp
gdb ./a.out

设置断点b 11

分别显示结构体中每个变量的地址,其结果如下图所示。
请添加图片描述

我们通过图可知,结构体中变量a的首地址为0x7fffffffdde4和结构体的首地址一致,其偏移量为0。结构体中变量b的首地址为0x7fffffffdde8,其偏移量为该首地址减去结构体首地址为0x7fffffffdde8-0x7fffffffdde4 = 4,其偏移量为4,同理可得,变量c相对于首地址的偏移量为8。

自身对齐字节数是对变量执行sizeof()函数得到结果,即是该变量占用空间的字节数。

系统有效对齐字节数是由系统设置的,一般在64位编译器下默认数字为16,可以进行修改。

自身有效对齐字节数min(自身对齐字节数,系统有效对齐字节数)

最大自身对齐字节数max(所有变量的自身对齐字节数)

以结构体A为例,计算其占用字节。

第一步,该结构体如下时

struct A{
    char a;
}

结构体中变量a的偏移量为0,自身有效对齐字节数为min(1,16)也就是1,根据规则2,其符合偏移量是自身有效对齐字节数的倍数。同样,其最大自身有效对齐字节数为1,当结构体大小是1的时候,符合规则3,所以其结构体大小为1。

测试程序example_1.cpp如下所示

#include <iostream>
using namespace std;
struct A{
    char a;
};
int main()
{
    A a;
    cout << sizeof(a) << endl;
    return 0;
}

运行得到的成果如下图所示:
请添加图片描述

第二步,在第一步结构体新增变量b

struct A{
    char a;
    int b;
}

在第一步结构体的基础新增了变量b,变量b相对于首地址的偏移量为1,其自身有效对齐字节数为min(4,16)也就是4,不符合规则2,需要在变量a后面填充空字节,使得变量b相对于首地址的偏移量为4,才能满足规则2。填充完毕后,其最大自身有效对齐字节数为4,此时结构体大小为8,符合规则3。

测试程序example_2.cpp如下所示

#include <iostream>
using namespace std;
struct A{
    char a;
    int b;
};
int main()
{
    A a;
    cout << sizeof(a) << endl;
    cout << (void *)&(a.a) << ":" << (void *)&(a.b) << endl;
    return 0;
}

其运行结果如下图所示:

请添加图片描述

第三步,该结构体在第二步的结构体基础上添加变量c。

struct A{
    char a;
    int b;
    short c;
}

在第二步结构体的基础新增了变量c,变量c相对于首地址的偏移量为8,其自身有效对齐字节数为min(2,16)也就是2,符合规则2。最大自身有效对齐字节数为4,此时结构体大小为10,不符合规则3,需要在变量c后方填充2字节来满足结构体大小是最大自身有效对齐字节数的整数倍,填充后,结构体的大小为12。

测试程序example_3.cpp如下所示

#include <iostream>
using namespace std;
struct A{
    char a;
    int b;
};
int main()
{
    A a;
    cout << sizeof(a) << endl;
    cout << (void *)&(a.a) << ":" << (void *)&(a.b) << ":" << (void *)&(a.c) << endl;
    return 0;
}

其运行结果如下图所示:
请添加图片描述

结构体字节对齐的整个流程就是这样,如果有疑惑的,欢迎评论区讨论。

  • 16
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值