C++成员对齐方式探讨
本文参考了《高质量程序设计指南——C++/C语言》一书
有不妥之处恳请指正
一、自然对齐
某些基于RISC(精简指令集)的CPU比如SPARC、PowerPC等,采用高字节和高字在低地址存放、低字节和低字在高地址存放的大端模式存储,并且把最高字节的地址作为变量的首地址。在这种自然的存储格式中,要求变量在内存中的存放位置必须自然对齐,否则CPU会报告异常。所谓自然对齐,就是基本数据类型(主要是short、int、double)的变量不能简单地存储于内存中的任意地址处,它们的起始地址必须能够被它们的大小整除。例如,在32位平台下,int和指针类型变量的地址应该能被4整除,而short变量的地址应该都是偶数,bool和char则没有要求。所以,基于这种CPU架构的平台,编译器将按照自然对齐的要求来为每个变量生成逻辑地址,C++/C编译器亦如此。
而Intel系列CPU采用小端模式来存放基本类型变量,即低字节和低字在低地址存放、高字节和高字在高地址存放,并且把最低字节的地址作为变量的首地址。在Intel系列CPU这种硬件平台上,并不严格要求基本数据类型变量在内存中必须自然对齐,同样也不会要求复合类型变量必须自然对齐。但是VC++在处理程序时,为了提高CPU的处理速度,对复合类型变量做了成员对齐处理。
二、成员对齐
VC++在处理复合数据类型成员变量时默认遵循以下三种原则:
(1) 复合类型对象的首地址要能被占用字节数最多的成员变量的字节数整除。
(2) 复合类型对象占用的内存空间大小要能被占用字节数最多的成员变量的字节数整除。
(3) 各成员变量存放的起始地址相对于复合结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
举例如下:
structexm
{
bool a;
double b;
bool c;
};
它是怎么存储的?
首先,在内存中找一个能被8整除的起始地址,然后分配24个字节。
为什么是24个而不是10个?或者是16个?
这是因为原则(3)的缘故,变量b占8个字节,那么它相对于结构的起始地址的偏移量必须为8的倍数,因此虽然a只占一个字节,但后面会补上7个字节。
那么为什么变量c也占8个字节啊?这是因为原则(2)的缘故。因为在结构体exm中,占用字节数最多的是变量b,8个字节,所以exm占用的空间大小应能被8整除,现在a和b已经占用16个字节了,而c至少要占用一个字节,因此会在c后补上7个字节,变成24个字节。
把exm成员调换一下,变为:
structexm
{
double b;
bool a;
bool c;
};
它的大小就变成16个字节了。
当然,我们也可以在程序中指定对齐方式,这时上述原则将不再成立。如果在程序中指定对齐方式,则对于复合结构中的变量,如果占用空间不足指定对齐字节,且如果按照默认对齐方式占用的空间将比指定的对齐方式大,则将按指定对齐方式补足,例如:
#pragmapack(4) //按照4字节边界对齐
structexm
{
bool a;
double b;
bool c;
};
在VS2010中,它占用16个字节空间。
分析:变量a如果按照默认对齐方式要占8位,但它本身是bool型变量,占一位,指定对齐方式为4字节对齐,则a将被补齐为4字节。同理变量c也一样。
把exm成员换一下位置:
#pragmapack(4) //按照4字节边界对齐
structexm
{
double b;
bool a;
bool c;
};
则exm占12位。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。