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个问题:
- C++11标准中,alignas不是最小设置8字节对齐,也就最小是alignas(8),否则设置无效吗?为什么还会出现上面的结果呢?
- 如果
alignas(4)
设置生效,根据第二节的例子中提到的:结构体的有效对齐值是自身对齐值和指定对齐值中较小者。那么实际存储情况像下面这样(sizeof(Info)= 6
)啊:
是因为C和C++标准不一样吗?
后续:经过验证,我得到了答案。
参考链接: