内存对齐问题

来源:互联网 发布:编辑书的软件 编辑:程序博客网 时间:2024/04/29 21:50

对于32位机器,寄存器是32位的,有32根数据线,而内存是按8位编址的,即一个内存单元是8位的。CPU读内存中数据时是一次读32位的即一次读连续4个地址的数据。你可能就会疑问了,像short类型数据占2个字节,假如有一个short型变量a存在地址0x020x03中,那CPU怎么读a呢,其实CPU会一次性读从0x00~0x03四个地址中的数据,然后将0x000x01中的数据“剔除”掉。好,你可能会问,为什么是读从0x00~0x03中的数据呢,而不是读从0x01~0x040x02~0x05呢?然后将其中多余地址的剔除呢?其实是这样的,CPU读写数据时,不仅每次读写的数据大小(读取粒度)是固定,而且对地址也是也有限制的,每次读取的内存数据的起始地址必须是4的倍数,如从0x000x040x08...开始访问。现在假如有一个short型数据存在0x030x04中,那么CPU在读取这个数据时必须分两次读,第一次读地址0x00~0x03,第二次读地址0x04~0x07,然后分别将0x03地址和0x04地址数据组合成一个数据。

我们知道可以通过#pragma pack(n)来改变数据的对齐方式。

#pragma pack(push) //保存对齐状态#pragma pack(4) //设定为4字节对齐struct test{char m1;double m4;int m3;};#pragma pack(pop) //恢复对齐状态

以上结构体的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1大小为1个字节。接着开始为m4分配空间,这时其偏移量为4,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于4,m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(8),那么我们可以得到结构的大小为24

但要注意以下几点:

1. CPU不会因为你的pack设置而改变自己的访问方式,你代码里对齐值是多少,CPU并不不知道

2. CPU在访问对齐数据和没有对齐的数据的时候会使用不同的方法,不同的效率。CPU可以访问放在奇数地址的4字节数据,只是效率上与放在4的倍数地址上的4字节数据不同。(效率是指时间和空间上的)

3. 对齐的数据是指数据所在地址能被数据大小整除。如:1个字节的数据在任何位置都是对齐的。2个字节的数据在偶数地址上是对齐的。4个字节的数据在4的倍数地址上是对齐的。

以上是针对CPU而言的,和编译器无关。

而编译器的pack开关语句改变的是你代码中数据的对齐方式,这种方式上的改变会造成有的数据对齐而有的数据可能没对齐。

比如4字节的数据放在了奇数地址。对CPU而言,奇数地址上的4字节数据是没有对齐的,CPU在访问的时候效率上有不同。

pack开关影响的是数据在内存中的排列方式。CPU可以访问任何对齐形式的数据,只是效率不同。



原创粉丝点击