由于,C/C++中结构体和类的内存模型基本上是一样的,所以我们这里只讨论类是怎么样的。结构体也是采用相统的原则
我们都知道,在类中,如果我们用sizeof()去查看它的大小会返回这个类的所占的字节大小,但是这个大小并不是直接叠加的。比如我们知道:
一个 int 类型占用4字节,一个char类型占用一个字节,可是他们两个放在同一个类中,他们的大小并不是 4+1=5字节。
而是8字节,这是因为编译器对存放在类中的这两个成员变量进行了补齐。
那么补齐的规则是什么?这里废话不多说,直接说结论:
C++ 中按照类中最大成员变量的字节数决定粒度,然后进行补齐。但是要注意,最大粒度就到8字节
**
这个机制叫做译注(alignment): alignment:就是将数值调整到某数的倍数,在32位计算机上,通常alignment位4bytes(32位) ,以此来使得bus的传输效率最大
**
举几个例子说明:
在这里,对于class A来说,它的最大成员变量也就是a,字节数是8,所以编译器会决定存取class A中成员变量的时候会用8字节作为粒度,对于字节数仅仅位1的b,也会补齐位8字节。所以一共占用16字节。
这个例子中,我们一直 string类的字节数占用40字节,b占用一个字节,编译器会以str作为最大粒度,可是最大粒度只有8字节(我想这应该是因为64位机器的存取最大粒度就到8字节,比如对于存器粒度最大位128字节的GPU来说,可能就不是这样)
因此编译器按照8字节的粒度,58+8=48
在这个例子中,我们知道str占用2字节,b占用3字节。可是char的粒度是1字节,而short的粒度是2字节,所以它依然会按照粒度位2来做补齐。b会被以22进行填充,最终结果就是2*2+2=6
在这个例子中,class A最大成员变量的粒度是4字节,但是m_x,m_z的大小,刚好可以达到一个4字节的粒度。
所以它们加起来,4*2+4=12
**
关于字节对齐的底层原理:
**
进行补齐的根本原因是因为CPU存取效率的因素,CPU理论上是可以访问内存中的所有地址。
可是对数据的存取并不是按照一个字节一个字节来进行的,而是一次4个字节或一次8个字节,因为这样可以一次存取多个字节,带宽比较高,对于GPU这种需要追求高带宽的计算设备来说,它的存取带宽可以达到128字节
假设我们的存取粒度是8字节,那么CPU在内存中寻址的起始地址,就必须是8的倍数,也就是0,8,16…
如果我们不进行字节对齐。就有可能出现这种情况,假设我有一个8字节的数据a,如果a的其实地址是0,那么CPU只需要一次就能对它进行存取,如果它的起始地址是1~7,那么CPU就需要两次才能对它进行存取。
所以字节对齐,是为了要提高效率。
因此C++会将比较小的数据进行填充,占用需要的空间,一次来达到对齐的目的