内存对齐是任何一个学习底层语言的程序员都要遇到的很关键的问题,尤其是计算
struct
这种复合数据类型大小时,就不再是简单的成员大小相加。这是因为计算机需要内存对齐导致的。本篇博文将对内存对齐进行深度解释,从根源上解决疑问。
数组和结构体
数组
数组属于复合结构类型,它是相同类型元素的结合,所以一旦数组定义完成,其成员类型都是同种类型,可以想象成线性紧密排列。所以计算一个数组大小时,它总是跟我们预期的大小一致。结构体
结构体不同于数组,为了解决实际中遇到的问题,它可以将多种类型聚合在一起,但是因为每个成员大小比如int
,char
,double
等等。(每个类型的大小不一,所以它不能够通过下标或者成员指针类型加减的方式访问每个成员,它通过成员名字访问),当用sizeof计算对应结构体大小时,绝大多数情况它总是跟我们预期的大小有出入,且永远是偶数。
为什么会这样?这就涉及到了计算机内存对齐。在寻找答案的过程中,一般解释都是仅仅对内存对齐的规则,地址分配规定再加上高深莫测术语上进行阐释(在此就不再赘述),并未对内存对齐的种种疑问做出很好的解释,所以我决定自己钻研解决。
在经过对CPU和DRAM的发展、框架、构造、连接等原理上的了解和思考后
大致明白了cpu寻址过程、内存中的数据的存放规则、片选信号、分时传输机制等等。对编译器对内存地址分配的规则,实际数据在内存中的布局有了知其所以然的快感。
现在通过我的问题主线,一步一个脚印还原主要问题出现和解决。
1.未对齐地址的数据对读取的影响
当前认知:
- 此cpu有32位数据总线,32位地址总线
- 数据总线每次访问可以直接提取32 bits的数据也就是4个字节
- 地址总线可以寻址2^32(4G)个地址单位,一个字节对应一个地址。
源:在访问未按4倍数对齐的地址读取四字节数据时,cpu的读取过程如下
假设要从地址1处访问四个字节,解释是cpu将在第一个总线周期先获得0~3地址的内容RED
,然后再第二个总线周期获取4~7地址处的内容GREEN
。然后移出RED
的第一个字节,再移出GREEN
的后三个字节,然后将数据整合到寄存器中,所以这样再未对齐的地址处访问4字节的数据就有了两次的总线周期。导致时间加长。
但一个简单的例子显得说服力微乎其微。
那么现在通过以下代码对应测试16位,32位CPU在不同地址上处理size个字节数据的耗时。
16位cpu操作
void CPU16( void *data, int32_t size ) //size表示要操作的字节数
{
int *data16 = (int*) da