您的位置:首页 > 其它

预处理-分节7(如何避免内存对齐的影响)

2016-10-11 13:36 274 查看
那么,能不能既达到提高性能的目的,又能节约一点空间呢?有一点小技巧可以使用。

 比如我们可以将上面的结构改成:

struct TestStruct2 {

char c1; char c2; short s; int i; };
这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。

在这个例 子中,sizeof(TestStruct2)的值为8。这个技巧有一个重要的作用,尤其是这个结构作为API 的一部分提供给第三方开发使用的时候。

第三方开发者可能将编译器的默认对齐选项改变, 从而造成这个结构在你的发行的DLL 中使用某种对齐方式,而在第三方开发者哪里却使用 另外一种对齐方式。

这将会导致重大问题。

比如,TestStruct1 结构,我们的DLL 使用默认对齐选项,对齐为

c1 00000000, s 00000002, c2 00000004, i 00000008,同时sizeof(TestStruct1)的值为12。 而第三方将对齐选项关闭,导致

c1 00000000, s 00000001, c2 00000003, i 00000004,同时sizeof(TestStruct1)的值为8。

除此之外我们还可以利用#pragma pack()来改变编译器的默认对齐方式(当然一般编译器 也提供了一些改变对齐方式的选项,这里不讨论)。

使用指令#pragma pack (n),编译器将按照n 个字节对齐。
使用指令#pragma pack (),编译器将取消自定义字节对齐方式。

在#pragma pack (n)和#pragma pack ()之间的代码按n 个字节对齐。

但是,成员对齐有一个重要的条件,即每个成员按自己的方式对齐.也就是说虽然指定了 按n 字节对齐,但并不是所有的成员都是以n 字节对齐。

其对齐的规则是,每个成员按其类型 的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n 字节)中较小的一个对齐,

即:min( n, sizeof( item )) 。并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空 字节。看如下例子: #pragma pack(8) struct TestStruct4 {

char a; long b; };

struct TestStruct5 {

char c;

TestStruct4 d; long long e; };

#pragma pack() 问题:

A),sizeof(TestStruct5) = ?

B), TestStruct5 的c 后面空了几个字节接着是d?
TestStruct4 中,成员a 是1 字节默认按1 字节对齐,指定对齐参数为8,这两个值中取1,a 按1 字节对齐;

成员b 是4 个字节,默认是按4 字节对齐,这时就按4 字节对齐,所以 sizeof(TestStruct4)应该为8;

TestStruct5 中,c 和TestStruct4 中的a 一样,按1 字节对齐,而d 是个结构,它是8 个字节,它 按什么对齐呢?

对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大 的一个, TestStruct4 的就是4.

所以,成员d 就是按4 字节对齐.成员e 是8 个字节,它是默认按8

字节对齐,和指定的一样,所以它对到8 字节的边界上,这时,已经使用了12 个字节了,所以又添
加了4 个字节的空,从第16 个字节开始放置成员e.这时,长度为24,已经可以被8(成员e 按8 字节对齐)整除.这样,

一共使用了24 个字节.内存布局如下(*表示空闲内存,1 表示使用内存。

单位为1byete): a b

TestStruct4 的内存布局:1***,1111, c TestStruct4.a TestStruct4.b d

TestStruct5 的内存布局: 1***, 1***, 1111, ****,11111111 这里有三点很重要:

首先,每个成员分别按自己的方式对齐,并能最小化长度。

其次,复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂 类型时,可以最小化长度。

然后,对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保 证每一项都边界对齐。
补充一下,对于数组,比如:char a[3];它的对齐方式和分别写3 个char 是一样的.

也就是说 它还是按1 个字节对齐.如果写: typedef char Array3[3];Array3 这种类型的对齐方式还是按1 个字节对齐,而不是按它的长度。

但是不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个。 另外,注意别的#pragma pack 的其他用法:

#pragma pack(push) //保存当前对其方式到packing stack #pragma pack(push,n) 等效于 #pragma pack(push)

#pragma pack(n) //n=1,2,4,8,16 保存当前对齐方式,设置按n 字节对齐

#pragma pack(pop) //packing stack 出栈,并将对其方式设置为出栈的对齐方
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: