记录C++字节对齐的标准,避免老是忘记;
为何需要对齐
- 为了运行效率;电脑处理器读取内存是以2,4,8等2的幂次方的字节chunk来进行读取的,假如数据没有进行字节对齐,处理器可能就需要进行两次内存读取才能还原原来的数据,从而大大影响效率;在某些x86的某些设置下,或某些处理器下,甚至会直接触发指令错误;
- 满足原子操作的条件:假如数据没有对齐,需要两次内存读取,那么就可能遇到两个内存分别处在不同的虚拟内存页上,假如一个内存页没有加载,就会触发页面中断,再运行页面加载指令,从而打断了原子操作的要求;
- 缓存友好:某些CPU的cacheline为64byte,满足字节对齐可以更容易塞满缓存,充分利用缓存大小;
基于此,各大处理器架构提出了相似的对齐要求,用行话来说来说就是自对齐,char数据可以是任意字节地址,2 byte short数据地址必须能被2整除,4 byte int数据地址必须能被4整除,32 bit指针要进行4byte对齐,64 bit指针要进行8byte对齐;
编程中的对齐
幸运的是,编译器会自动帮我们进行处理,来使得以上对齐规则得以满足;
1. 针对包含不同数据类型的结构体,编译器可能会主动在数据之间插入padding,来使得结构体内部的各个数据类型能够满足对齐要求; 如下:
|
|
2. 除了保证结构体内部的数据类型对齐,对于结构体本身也有对齐要求,就是结构体字节大小需要为结构体内部最大数据类型字节数的倍数,该对齐要求能够保证申请结构体数组时,数组内各结构体的各个数据成员都能满足上面提到的数据对齐要求; 如下:
|
|
其实道理也很简单,我们申请一个数组为Data data[2],展开后内部的数据分布为
|
|
假如没有padding,数据a1就不能满足数据对齐要求,从而导致各种延伸问题;
编程中面对对齐应该如何做
知道了对齐规则,我们在编程中应该如何操作才能更好的利用对齐;注意点有两点:
1. 注意结构体内部数据的排序,使得结构体大小最小,从而能够节省系统内存; 例如:
|
|
以上两个结构体,内数存储数据是一样的,但是Data2使用了更好的排序方式,从而使得结构体占用内存更小;
2. 如果结构体中在编译时会添加padding,不如在申明时手动添加padding,从而能使代码更加清晰; 例如:
|
|
以上两个结构体,所占用字节大小是一样的,但是Data2使用了显式的padding,从而使得代码使用者能够一眼看清内部数据成员数量,一个所占用字节大小;
关于更复杂情况下的字节占用计算,推荐阅读类大小计算;