预处理-分节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 出栈,并将对其方式设置为出栈的对齐方
比如我们可以将上面的结构改成:
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 出栈,并将对其方式设置为出栈的对齐方
相关文章推荐
- 如何避免内存泄漏、溢出的几种常用方法
- iphone 使用popViewController如何避免内存泄露
- 如何在.net应用中发现和避免内存和资源泄露
- PHP CURL如何处理上传内存中文件,避免磁盘IO开销
- 内存对齐方式的总结 如何计算结构体所占内存的大小
- 4类JavaScript内存泄露及如何避免
- 如何避免广告投放这把“双刃剑”的影响
- 如何避免JQuery Dialog的内存泄露
- iOS 如何避免内存泄露
- 如何在iPhone应用中避免内存泄露
- 内存溢出和内存泄漏的区别和如何避免内存溢出
- 如何避免由于使用Handler导致的内存泄露
- 如何避免JavaScript的内存泄露及内存管理技巧
- 【Android Advanced Training - 05】传输数据时避免电量的浪费[Lesson 1 - 看无线电波如何影响网络操作]
- 如何避免网站更换服务器带来的影响?
- list与vector容器如何处理才能避免内存增长?
- 如何避免JDBC内存溢出问题
- 在iPhone应用中如何避免内存泄露
- 如何在你的C/C++代码中避免、发现(修复)内存错误
- 4类JavaScript内存泄露及如何避免