一、什么是字节对齐
对下面的类:
class B {
public:
int m_a;
short int m_b;
double m_c;
private:
int m_d;
public:
int GetValue() { return m_c; }
};
类B对象的大小,如果直接计算是18(4+2+8+4),但是sizeof(B)结果是24。
多出来的6个字节是怎么回事呢?其实是内存对齐的原因。编译器在默认的情况下,分配给各个成员变量的内存大小似乎是向占最大空间的成员变量对齐的(这里我不敢肯定,还没看到权威的说法)。在B类中编译器首先为m_a分配空间,编译器一次为m_a分配8个字节(与最大成员m_c对齐),实际上m_a只占4个字节,还有4个字节多。接着编译器为m_b分配空间,经检查,m_b只占2字节,刚好前面还有4个字节多,所以m_b就放在前面多出的那4个字节空间中,现在已经为m_a和m_b分配了空间,但是m_a加上m_b也就只有6字节,还有2个字节多。如果下面分配的变量刚好是2字节的话,那就刚刚好装满8个字节,没有浪费空间,可是下面m_c是个double类型的变量,要占8个字节,显然2个字节是装不下的,因此编译器再为m_c分配了8个字节的空间。刚装满,接下来又为m_d分配空间,根据之前的规则,编译器分配给m_d的空间也是8字节。这样看来,编译器总共为B的对象分配了8+8+8=24字节的空间。如下图所示:
分配时,以最长字节(double m_c的8字节)为最小分配单元。Sizeof(B)为8字节的整数倍。
二、为什么需要进行字节对齐
可能你觉得编译器这样做是浪费内存空间,但实际上这样做是很适合CPU做一些指令操作的。
1、各个硬件平台对存储空间的处理不尽相同,比如一些CPU访问特定的变量必须从特定的地址进行读取,所以在这种架构下就必须进行字节对齐了,要不然读取不到数据或者读取到的数据是错误的。
2、会对CPU的存取效率产生影响:比如有些平台CPU从内存中偶数地址开始读取数据,如果数据起始地址正好为偶数,则1个读取周期就可以读出一个int类型的值,而如果数据其实地址为奇数,那我们就需要2个读取周期读出数据,并对高地址和低地址进行拼凑,这在读取效率上显然已经落后了很多了。
一句话:用空间效率来换时间效率。
如果还是觉得空间比较重要,那么可以通过设置编译属性或使用编译器指令#pragma来指定编译器所做的对齐方式。
例如语句:#pragma pack(1)就是设置向1字节对齐。这时使用sizeof(B)得出的结果就是18了。
参考资料:
http://blog.csdn.net/tmljs1988/article/details/8051171
http://blog.sina.com.cn/s/blog_67c294ca01012qbu.html
http://www.cppblog.com/snailcong/archive/2009/03/16/76705.html