内存对齐

来源:互联网 发布:淘宝保证金计划5000元 编辑:程序博客网 时间:2024/06/05 14:57

一个常见的例子

struct s1{    char a;    char b;    int c;}struct s2{    char a;    int c;    char b;}

sizeof(s1) = 8;sizeof(s2) = 12;

内存对齐

内存地址对齐,是一种在计算机内存中排列数据、访问数据的一种方式,包含了两种相互独立又相互关联的部分:基本数据对齐和结构体数据对齐

对于基本数据类型,其地址是内存对齐的:基本类型数据对齐就是数据在内存中的偏移地址必须等于一个字的倍数
而结构体对齐:为了保证访问结构体数据的性能,可能需要在上一个数据结束的地方和当前数据开始的地方插入一些纯粹占位的字符。包括结构体内部元素类型的对齐和结构体本身的对齐

内存对齐的规则为:

1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
2、 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

看上面中s2的例子,假设编译默认pack(8), a偏移0,c为int,所以其偏移应该是是min(8,4)=4 ,所以需要在a,c之间插入3个填充字节,再看b,其偏移为min(8,1)=1;所以不用考虑。已有的数据占了1+3+4+1=9字节。现在考虑第二条规则,结构体本身也要对齐(这样是为了当声明结构体数组时,能够访问更快),最大的数据成员为int,4字节,min(8,4)=4,所以要加上3字节,9+3=12凑成4的倍数。所以sizeof(s2)=12;
所以上面结构体实际为

struct s2{    char a;    char pad[3];    int c;    char b;    char pad[3];}

那么一个例子
struct{ char a; char b;} 其 size是2bytes. 而不是一定要填充到4字节。因为根据第二条,对齐按char的数据长度来,就是char的倍数即可,所以是 2bytes.

注意数组元素之间是没有填充位的,加了反而会浪费空间,无论声明基本元素还是结构体元素的数组。如果是类型长度小于等于4字节,那么不需要加填充位,因为一次访问一个字就可以把元素取出来,如果大于4字节,那么本来就需要多次访问。
为了优化,重新排列结构体元素顺序或者故意加一些填充位也是可以的。比如OpenCV的图像像素,行首的地址通常需要字节对齐,所以行与行之间的偏移 widthStep不一定等于width*channels, 而是 widthStep = (width*channels + align-1) /align *align;

可以用 #pragma pack来改变编译器默认的内存设置

#pragma pack(push) #pragma pack(2)// your struct define#pragma pack(pop)

内存对齐的好处

  • 加快数据的访问速度。

计算机内部32位的地址总线,32位的数据总线,所以每次能读取4个字节的数据,而并非一个字节一个字节读取。最低2位A[0],A[1]是不用于寻址,A[2-31]才能存储器相连,因此只能访问4的倍数地址空间
假设一个int 4字节,如果其地址是对齐的(假设是0x3FFFFFF0),那么只需要一次访问内存就能获取4个字节,但是如果其地址是非对齐的(比如0x3FFFFFF1),那么需要连续2次访问内存,分别访问0x3FFFFFF1获得后面3个字节和0x3FFFFFF4获得前面1个字节,并合起来,然后才能获取这个int数据。而由于CPU的处理速度远高于访问内存的速度,所以显然前者要快很多

  • 便于程序移植。而在有些CPU架构上,并不支持非对齐地址的访问,所以如果代码中字节不对齐,对程序移植也会有影响

参考:
内存地址对齐
内存对齐的规则以及作用

0 0
原创粉丝点击