内存对齐原理
2013-10-16 19:33
218 查看
看到学长的博客有一个有关内存对齐原理的小程序,楞的看不懂是什么意思。
留下来等待以后的解决。必须要解决。
输出的结果是:st1=12
st2=8
很是不理解,如果没有找到内存对齐原理的话,应该就就st1=st2=7。但是,不是这样的。
-------------------------------------------------------------------------------------------------------------------------------
2015.4.27终于遇到这个问题了,现在也正是学习这个的时候,来解决下。
首先我们来看一下内存对齐的规则:
对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
#pragma pack(n) 表示设置为n字节对齐。(VC默认8字节对齐)
在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
以程序1为例解释对齐的规则:
St1 :char占一个字节,起始偏移为0 ,int 占4个字节,min(#pragma pack()指定的数,这个数据成员的自身长度) = 4(VC6默认8字节对齐),所以int按4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4,在char后编译器会添加3个字节的额外字节,不存放任意数据。short占2个字节,按2字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。到此规则1的数据成员对齐结束,此时的内存状态为(x表示额外添加的字节):
oxxx|oooo|oo
0123 4567 89 (地址)
共占10个字节。还要继续进行结构本身的对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,st1结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,需添加2个额外字节使结构的总大小为12 。此时的内存状态为:
oxxx|oooo|ooxx
0123 4567 89ab (地址)
到此内存对齐结束。St1占用了12个字节而非7个字节。
性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。
图一:
这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。
图二:
CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。
假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:
数据从0字节开始
数据从1字节开始
再次假设内存读取粒度为4。
图三:
当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。
当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。
图四:
此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。
这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。
---------------------------------------------
今天遇到了一个关于内存对齐的面试题:
解释一下:
我们知道结构体的数据成员进行对齐iVersion(0 - 3), cTag(4), cAdv(5), iUser(8 - 11), cEnd(12)。
结构体对齐:4 * 4 >= 13 所以 ans = 16.
留下来等待以后的解决。必须要解决。
<span style="font-size:18px;"><span style="font-size:18px;">#include<iostream> using namespace std; struct st1 { char a; int b; short c; }; struct st2 { short c; char a; int b; }; int main() { cout<<"st1="<<sizeof(st1)<<endl; cout<<"st2="<<sizeof(st2)<<endl; return 0; } </span></span>
输出的结果是:st1=12
st2=8
很是不理解,如果没有找到内存对齐原理的话,应该就就st1=st2=7。但是,不是这样的。
-------------------------------------------------------------------------------------------------------------------------------
2015.4.27终于遇到这个问题了,现在也正是学习这个的时候,来解决下。
内存对齐原理
对于大多数的程序员来说,内存对齐基本上是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体大小的不同。那么编译器为什么要进行内存对齐呢?程序1中结构体按常理来理解sizeof(st1)和sizeof(st2)结果都应该是7,4(int) + 2(short) + 1(char) = 7 。经过内存对齐后,结构体的空间反而增大了。首先我们来看一下内存对齐的规则:
对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
#pragma pack(n) 表示设置为n字节对齐。(VC默认8字节对齐)
在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
以程序1为例解释对齐的规则:
St1 :char占一个字节,起始偏移为0 ,int 占4个字节,min(#pragma pack()指定的数,这个数据成员的自身长度) = 4(VC6默认8字节对齐),所以int按4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4,在char后编译器会添加3个字节的额外字节,不存放任意数据。short占2个字节,按2字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。到此规则1的数据成员对齐结束,此时的内存状态为(x表示额外添加的字节):
oxxx|oooo|oo
0123 4567 89 (地址)
共占10个字节。还要继续进行结构本身的对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,st1结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,需添加2个额外字节使结构的总大小为12 。此时的内存状态为:
oxxx|oooo|ooxx
0123 4567 89ab (地址)
到此内存对齐结束。St1占用了12个字节而非7个字节。
三、内存对齐的作用
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。
图一:
这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。
图二:
CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。
假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:
数据从0字节开始
数据从1字节开始
再次假设内存读取粒度为4。
图三:
当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。
当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。
图四:
此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。
这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。
---------------------------------------------
今天遇到了一个关于内存对齐的面试题:
struct _THUNDER{ int iVersion; char cTag; char cAdv; int iUser; char cEnd; }Thunder; int sz = sizeof(Thunder);
解释一下:
我们知道结构体的数据成员进行对齐iVersion(0 - 3), cTag(4), cAdv(5), iUser(8 - 11), cEnd(12)。
结构体对齐:4 * 4 >= 13 所以 ans = 16.
相关文章推荐
- c++内存对齐原理
- 内存对齐的原理,作用,例子以及一些规划(中英文说明,适用sizeof结构体)
- Linux中_ALIGN宏背后的原理(ZZ) -- 内存对齐
- “内存对界”原理——数据是如何对齐的
- Linux中_ALIGN宏背后的原理――内存对齐
- Linux中_ALIGN宏背后的原理(ZZ) -- 内存对齐
- 内存对齐的原理,作用,例子以及一些规划(中英文说明,适用sizeof结构体)
- C++中怎么求类的大小?以及内存对齐原理(面试官经常问到的问题)
- 结构体大小的计算及设置内存字节对齐数原理理解
- c++结构体内存对齐原理和方法
- 从内存的角度解释内存对齐的原理
- C++ 中内存对齐原理及作用
- 烦请哪位老大可以介绍一下C语言中的内存对齐的原理和实现?
- C语言内存对齐原理
- 面向对象继承中内存加载原理
- c/c++内存对齐技术之图文终极解释[原创]
- sizeof 操作符及pragma内存对齐
- 从0开始学模拟挂(一)--找内存基址,包含原理 _ 脚本..._按键精灵论坛
- 内存对齐与ANSI C中struct型数据的内存布局
- 计算机存储单位以及内存原理