知识背景:
字段对齐,使对象或成员的地址满足一定要求。4字节对齐就是地址都是4的整数倍,这个必须是2的N次方。
为什么要对齐?一方面,如果4字节对齐了,对于128个内存空间,实际只有32个独立地址,相当于管理地址变少了,文件系统经常这么用,用少的寻址空间,管理更大的磁盘空间。另一方面,一些硬件设计上了,为了一些考虑,强制要求地址要符合指定规则。
对齐有什么影响?最直接的影响就是占用内在大小变了,会影响sizeof,举例如下:
struct A
{
int a;
char b;
int c;
};
sizeof(A)大小并不是4+1+4=9,而是12,4+4+4。
为什么要改变对齐?强转之下,受对齐影响,不正确了。容易遇到的场景就是:收到网络字节流,消息头是结构化的,定义一个结构体,强转读取对应的字段
怎么更改对齐大小?
c++11之前,我们经常这么用
#pragma pack(push) //保存当前的对齐方式
#pragma pack(1) //指定接下来,1字节对齐
//定义你的结构体
#pragma pop(pop) //指定恢复保存的对齐方式
明显这个相当暴力了,如果每个人都这么做,至少3行代码。为了省事,总容易有人不push和pop,直接改,某人就会在不知情的情况下,包含了这个头文件,进而受影响……
于是,编译器突破规范,定义了扩展:__declspec(align(n))等,记住,是等,也就是各做各的,不统一,于是c++11火速规范化制定了这个标准:alignas 和alignof。
a开头的关键字,排在最前面,有人会郁闷吧,总看到,却不知道是什么,我从误区开始,逐步解释一下。
用法:alignas(t) define; t可以是个常量表达式,也可以是具体的大小,也可以是一个类型。 define是原本的定义语句,例如:
alignas(int) int b;
struct alignas(33) s{
int a;
};
alignof(type),和sizeof非常类似,只是返回对应类型的对齐大小,例如:
cout << alignof(s); //返回s的对齐方式
cout << alignof(s::a) ; //返回s成员的对齐方式
理论实践
知识讲完了,开始错误之路吧
一、对齐不是2的N次方
struct alignas(3) A //error: requested alignment is not a power of 2
{
char a;
char b;
char c;
char d;
};
二、理解错了对齐对象
struct alignas(2) A
{
char a;
char b;
char c;
char d;
};
if(sizeof(A) == 8)
alignas修饰结构体时,不是对齐结构体每个成员,而是对齐结构体本身,所以大小不是4*2=8,则是4不变。如果再增加一个char成员,则大小6, 不是5。
这样去理解结构体对齐:A对齐了,A+sizeof(A)是下一个A元素的地址,需要同样满足对齐,所以需要补空间1。
三、对齐低于结构体成员最大对齐
struct alignas(2) A
{
int a;
char b;
};
sizeof(A)不是6,而是8,因为a已经是4对齐了,所以b之后会补到8。 alignas(16)则大小是16。
四、分析一下复杂的,就毕业了,sizeof(B)是多少?128
struct alignas(32) A
{
int a;
alignas(8) char b;
};
struct B
{
A a;
A b;
alignas(1) char c;
A d;
};
以后,再怀疑字节对齐时,请用alignof输出实际的对齐方式看看,不用再去打印内存或调试了。