假如有个结构体A如下,请问它占用了几个字节?
struct A{
char a;
int b;
short c;
}
以我们的直观感觉,char
占用1个字节,int
占用4个字节,short
占用2个字节,因此总共占用7个字节。
真是这样吗?让我们运行实测一下,测试程序exampl.cpp
代码如下所示:
#include
using namespace std;
struct A{
char a;
int b;
short c;
};
int main()
{
A a;
cout << sizeof(a.a) << ":" <
每个结构体的占用空间如我们所知道的char
是1,int
是4,short
是2,累加结果应该为7,但最后结构体占用的字节却是12个,这个就是结构体的字节对齐机制。
结构体内变量的字节对齐需要符合以下规则。
计算结构体占用字节重点看规则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
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
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
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;
}
结构体字节对齐的整个流程就是这样,如果有疑惑的,欢迎评论区讨论。