1.为什么要进行c 内存对齐齐以及对齐规则

为什么要进行内存对齐以及对齐规则
时间: 15:57:32
&&&& 阅读:204
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&
#include&iostream&
using namespace
int main(){
cout&&sizeof(A)&&
cout&&sizeof(B)&&
以上结构体变量数量类型相同。但是sizeof却不同,
& & sizeof(A) is 12
& & sizeof(B) is &8
那么问题来了,为什么两个一样的结构体,但是sizeof大小却不同?
& &答案就是内存对齐导致结果不同
对于大多数程序员来说,内存对齐是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了不同的声明顺序的结构体大小不同。
那么编译器为什么要进行内存对齐呢?本来sizeof(int +char +short)应该是7,对齐之后反而变大了,为什么?
先来看看内存对齐的规则?
1.对于结构的各个成员,第一个成员位于偏移为0的位置,以后的每个数据成员的偏移量必须是 &min(#pragma pack()指定的数,这个数据成员的自身长度)的倍数
2.在所有的数据成员完成各自对齐之后,结构或联合体本身也要进行对齐,对齐将按照 #pragram pack 指定的数值和结构或者联合体最大数据成员长度中比较小的那个 &也就是 &min(#pragram pack() , 长度最长的数据成员);
& &#pragram pack(n) &表示的是设置n字节对齐,vc6默认的是8
接下来以第一个结构体为例说明上述规则?
A:char占一个字节,起始偏移为零,int 占四个字节,min(8,4)=4;所以应该偏移量为4,所以应该在char后面加上三个字节,不存放任何东西,short 占两个字节,min(8,2)=2;所以偏移量是2的倍数,而short偏移量是8,是2的倍数,所以无需添加任何字节,所以第一个规则对齐之后内存状态为 &0xxx|0000|00 & &
& 此时一共占了10个字节,但是还有结构体本身的对齐, min(8,4)=4;所以总体应该是4的倍数,所以还需要添加两个字节在最后面,所以内存存储状态变为了 0xxx|0000|00xx & 一共占据了12个字节
接下来我们好好讨论一下内存对齐的作用?
& 1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
& 2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。具体原因接下来解释
我们普通程序员心中的内存印象,由一个个字节组成,但是CPU却不是这么看待的
cpu把内存当成是一块一块的,块的大小可以是2,4,8,16 个字节,因此CPU在读取内存的时候是一块一块进行读取的,块的大小称为(memory granularity)内存读取粒度。
我们再来看看为什么内存不对齐会影响读取速度?
& & 假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:
& & & & & &1.数据从0字节开始
   & & &2.数据从1字节开始
解析:当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成了。
& & & & 当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,4,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。
& & &但是这还属于乐观情况,上文提到内存对齐的作用之一是平台的移植原因,因为只有部分CPU肯干,其他部分CPU遇到未对齐边界就直接罢工了。
参考图片:
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文:http://www.cnblogs.com/jijiji/p/4854581.html
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
家境小康, 积分 1071, 距离下一级还需 929 积分
论坛徽章:11
求教给位,内存对齐是什么原理,memalign()和posix_memalign()函数一直看不懂,请指教。
论坛徽章:1
你指的内存对齐原理是什么意思?
是为什么需要内存对齐吗?
大富大贵, 积分 11659, 距离下一级还需 8341 积分
论坛徽章:11
本帖最后由 Ager 于
23:47 编辑
对齐是一种策略。
估计楼主是在问这种策略的缘起。
这种策略,打个比方 ——
有家小店的店主怕麻烦,遂贴出告示曰:本店概不受理分币!
那么,这就是“以角币对齐”。
可用积分 -2
不知道你在讲啥。
论坛徽章:0
1,对齐的实现不算复杂,把起始地址做后向偏移,使得返回的内存空间的起始地址为指定对齐数目的倍数,也因为有这个偏移操作,所以总共使用(或者说加上浪费的)内存会大于或等于实际使用的空间。
2,为什么要做对齐,这主要是从性能上考虑,以X86为例,内存数据加载到CPU cache总是以一条cache line为基本单元,也就是说每一次CPU都会从内存读取64字节的数据到CPU cache,即便我们只使用了其它的一个字节。详情可参考:
论坛徽章:0
当然,有的cpu架构对内存的访问必须是对齐访问,由于了解不多,无法多说。
富足长乐, 积分 5182, 距离下一级还需 2818 积分
论坛徽章:0
对齐就是一个class A{};可能不是5字节。
丰衣足食, 积分 804, 距离下一级还需 196 积分
论坛徽章:1
可以自己写个东西,让申请回来的地址实行地址对其:
#define DOUNDARY8(ptr) (((char*)prt) + (0x8 - ((char *)ptr)&0x)
实行8字节对其。
家境小康, 积分 1071, 距离下一级还需 929 积分
论坛徽章:11
受教,多谢楼上几位!
另外问一句楼上file3同学,
会显示成图片中的样子,求庐山真面目~
(26.08 KB, 下载次数: 28)
11:22 上传
大富大贵, 积分 11659, 距离下一级还需 8341 积分
论坛徽章:11
本帖最后由 Ager 于
03:31 编辑
Ager 发表于
lenky0401 & & & & -2 & & & & 不知道你在讲啥。
斑竹大虾,您也忒严厉了吧,就算是我灌水也不至于倒扣2分吧……我在CU上本来就是一个千年穷光蛋,再这麽扣下去,我要成负翁了……呵呵……:)
既然您说“不知道我在讲啥”,那么,我就试着扯扯能让大家感到reasonable的东东吧……(声明一下,我下面纯属“扯”,尽管回帖批判,不要再扣血了……)
“内存对齐”其实算个俗称,比较严格地说法(尤其是在高级编程语言的视阈来看),应该叫“数据结构对齐”。本来嘛,内存都是死的,它自己又不会听口令排队。而“对齐”其实是我们施加在内存上的一套逻辑,一种策略。
之所以有这种策略,除了斑竹您说的,是缘自它是为了满足“性能要求”的必要条件之外,还有一个原因,就是这种策略,也是满足“程序在不同架构之间的‘可移植性’的要求”(某些硬件设施只能在存储器的特定位置处access特定类型的数据或结构)的必要条件之一。
下面综合上述两点,继续扯一下。
首先说一下Cache(特指所谓“CPU高速缓存”)。基本上,Cache在高级语言程序员的视角来看,是透明的。它是完全依赖硬件设施来实现的机制,高级语言一般无法直接干预它,也无法直觉察觉它是如何运行的(不过,可以利用它)。在x86家族中,最早实现Cache机制的,大约是在1985年诞生的80386,但在此前的x86家族成员中,在数据处理的策略上,对齐早就是被固有地运用的了。所以,对齐这个策略,跟Cache并没有直接的渊源关系。不过,在后来的运用中,高级语言编程下的数据结构对齐策略之矢,确是以Cache为的而放的。
再说一下“Cache Line”,它也叫“Cache Block”,即“缓存块”。在32位运算设施上,这个“块”包含了若干个具有连续地址的存储单元,通常,单元长度就是该设施的一个Word长度(或者说“按Word长度对齐” —— 可见,“对齐”的观念早于Cache的观念),即32 bits。不过在x86-32(Intel直接称呼它为IA-32)上,这里的概念有一些混乱。IA-32为了保持对之前的16-bits设施的兼容,“Word”还是16 bits的。x86-64亦然。那么,x86-32上的 Cache Line到底是多长?不管了,对于我们高级语言的程序员来说,关心的就是如何利用Cache。那么 cat /proc/cpuinfo |grep cache 一下就知道了。【—— 关于x86这方面的事情,补充说明与一些概念上的澄清,统统给一个汇总在下面的第16楼里面。】
那么,忽略了架构的区别和Cache不说,就单说“对齐”这件事情。
上面说了,“内存对齐”不妨称作“数据结构对齐”,那么这种策略,实际上是透过把握存储单元在存储器中的位置,即“地址”来实现的,所以,也可以被称作“地址对齐”。而地址这个东西,一些高级语言是可以把握的。下面来扯扯:
CPU在access存储器的时候,有一个“粒度”概念,即CPU在一个操作单位中,只能access一个单位长度的数据,不可再分割,所谓“粒子性”。Address& && & Memory& && && &&&Register
0& && && && &&&XH& && & -&& && &&&XH
1& && && && &&&XL& && & -&& && &&&XL
2& && && && &&&YH& && & -&& && &&&YH
3& && && && &&&YL& && & -&& && &&&YL
4& && && && &&&ZH
5& && && && &&&ZL
6& && && && &&&OH
7& && && && &&&OL复制代码上图的四个箭头,代表了CPU一次性地在内存中抓取了4个Bytes的数据到寄存器,即这个CPU的“内存access粒度”是4 Bytes的。
如果我们让CPU去抓取X这个数据(由XH+XL两个Bytes组成),那么我们就必须让access这一类数据(比如后来的Y、Z、O)的指针始终保持“从地址0开始的2 Bytes对齐”,即指针的取值只能是0、2、4、6。
否则,如果让指针不这样对齐,比如取值1开始access的话,那么,就会造成这种状况:Address& && & Memory& && && &&&Register
0& && && && &&&XH& && && && && &&&
1& && && && &&&XL& && & -&& && &&&XL
2& && && && &&&YH& && & -&& && &&&YH
3& && && && &&&YL& && & -&& && &&&YL
4& && && && &&&ZH& && & -&& && &&&ZH
5& && && && &&&ZL
6& && && && &&&OH
7& && && && &&&OL复制代码这样一来,数据X肯定是抓不完整了,寄存器里原来有2个有效数据,现在只剩了1个有效数据(即数据Y),还有2个无效的数据(X的XL部分和Z的ZH部分)。如此,至少寄存器的利用率就大大折损了。
早期的68000处理器的“内存access粒度”乃是2 Bytes的,如上图所示。该处理器没有有效的处理非对齐地址的机制,而那时候的Mac OS对处理器因非对齐地址而抛出的异常也没有良好的处理机制。所以,当遭遇了上图中的非对齐地址的情况时,用户就不得不去重新启动电脑了。
最后,回到高级语言编程的环境中来,再扯扯。
当我们在C中构造一个如下的结构的时候:struct baz {
& &&&// 假设为1个Byte
& && &// 假设为4个Bytes
& & // 假设为2个Bytes
};复制代码那么,baz类型的一个实例,将在内存中如下放置:Address& && & Memory& && && &
0x0000& && && & c& && && && && &
0x0001& && && &
0x0002& && && &
0x0003& && && &
0x0004& && && & i
0x0005& && && & i
0x0006& && && & i
0x0007& && && & i
0x0008& && && & s
0x0009& && && & s
0x000a
0x000b
0x000c& && && &
…… ……复制代码这样,如果CPU的“内存access粒度”合适的话,那么,CPU抓取每一个成员数据的时候,都可以一次性完成(原理同前所述)。
不过,我们也可以看出,存储器中的一些位置(以Bytes为单位)上没有存放什么“有用的数据”,比如在从0x3这3个Bytes上。所以,在某些“特别珍惜”数据所占空间的场合里面,这个实例的数据就有可能被“打包”,比如在某些网络传输过程中,该实例就有可能这样在数据空间序列中存在:Address& && & Memory& && && &
0x0000& && && & c& && && && && &
0x0001& && && & i
0x0002& && && & i
0x0003& && && & i
0x0004& && && & i
0x0005& && && & s
0x0006& && && & s
0x0007& && && & 牛头
0x0008& && && & 牛头
0x0009& && && & 猪面
…… ……复制代码这时候,数据存在的方式,不再是像之前那样的对齐的,而CPU去access数据的方式有可能还是按存储机构的地址对齐的。在这种情况下,如果我的意图是获取从0x0001开始的32 bits的数据,即实例的成员i的数据的话,那么,CPU有可能还是从0x0000抓取。如果CPU的“粒度”是4 Bytes,那么,它一次只能抓取到成员i的高3 Bytes的数据,(即从0x3上的,而0x0000上的数据没用),然后再来一次,抓取从0x7上的数据,其中只有0x0004上的数据有用,而从0x7上的成员s和半个牛头都没用。
所以,你可能就会想:有对齐,可真好呀——!
以上,仅供参考,呵呵:)
可用积分 +4
虽然有点混乱,但回答得很认真。
小富即安, 积分 3192, 距离下一级还需 1808 积分
论坛徽章:4
给楼上加2分吧,虽然还是没太看懂。
北京盛拓优讯信息技术有限公司. 版权所有 京ICP备号 北京市公安局海淀分局网监中心备案编号:22
广播电视节目制作经营许可证(京) 字第1234号
中国互联网协会会员&&联系我们:
感谢所有关心和支持过ChinaUnix的朋友们
转载本站内容请注明原作者名及出处ARM 内存对齐总结
> ARM 内存对齐总结
ARM 内存对齐总结
一、啥是内存对齐?为啥要内存对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问都可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就是对齐。字节对齐的原因大致是如下两条: 1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。本文引用地址:二、对齐规则 每个特定平台上的编译器都有自己的默认&对齐系数&(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的&对齐系数&。规则: 1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在偏移量为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行,即各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的整数倍。2. 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。 3. 结合1、2可推断:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。三、X86对齐实验 下面再简要回顾解释一下上述的对齐规则,结合实例进行分析: 1. 数据类型自身的对齐值:对于char型数据,其自身对齐值为1字节,对于short型为2字节,对于int,float,double类型,其自身对齐值为4字节。 2. 结构体的自身对齐值:其成员中自身对齐值最大的那个值。 3. 指定对齐值: #pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况,第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。 4. 数据成员和结构体的有效对齐值:数据成员(数据类型)和数据结构的自身对齐值和指定对齐值中小的那个值,数据成员对齐了数据结构自然也就对齐了。了解上述四个基本概念,我们开始讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N,就是表示&对齐在N上&,也就是说该数据的"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍)。下面结合VS2005中编译环境的例子进行深入了解:例子B分析:struct B{};假设B从地址空间0x0000开始排放。该例中没有显式指定对齐值N,VS2005默认值为4。 成员变量b自身对齐值是1,比指定或默认指定对齐值4小,故有效对齐值为1,其存放地址0x0000符合0x,满足字节对齐原则。 成员变量a自身对齐值为4,和指定或默认指定对齐值4相等,故有效对齐值也为4,为了保证字节对齐,成员变量a只能存放在起始地址为0x7这四个连续的字节空间中,复核0x。 成员变量c自身对齐值为2,比指定或默认指定对齐值4小,故有效对齐值为2,可顺序存放在0x9两个字节空间中,符合0x。 至此满足了数据成员的字节对齐,接着看数据结构B的对齐。数据结构B的自身对齐值为其变量中最大对齐值(也就是成员变量b)4,故结构体B的有效对齐值也是4。根据结构体圆整的要求, 0x0=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0xB 共有12个字节,sizeof(struct B)=12。 之所以在变量C补充2字节,是因为要实现编译器快速有效的存取结构数组,试想如果定义B结构数组,第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果不把结构的大小补充为对齐值(4)的整数倍,那下一个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了。例子C分析:__align(2) struct C{}; 同理,例子C中成员变量b自身对齐值为1,指定对齐值为2,故效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x,满足字节对齐原则。 成员变量a自身对齐值为4,指定对齐值为2,故有效对齐值为2,顺序存放在0x3、0x5四个连续字节中,符合0x,满足字节对齐原则。 成员变量c的自身对齐值为2,与指定对齐值相等,故有效对齐值为2,顺序存放在0x7中,符合 0x,满足字节对齐原则。 从0x07共八字节存放的是结构体C的变量。结构体C自身对齐值为4,比指定对齐值2大,故C的有效对齐值为2,因8%2=0,C只占用0x7的八个字节。所以sizeof(struct C)=8,完全满足字节对齐原则。除了指定的对齐值不同能导致数据结构的地址存放不同外, 编译器不同存放结构体方式也可能不同。四、ARM平台的对齐问题 在ARM中,有ARM和Thumb两种指令。 ARM指令:每执行一条指令,PC的值加4个字节(32bits).一次访问4字节内容,该字节的起始地址必须是4字节对齐的位置上,即地址的低两位为bits[0b00],也就是说地址必须是4的倍数。 Thumb指令:每执行一条指令,PC的值加2个字节(16bits).).一次访问2字节内容,该字节的起始地址必须是2字节对齐的位置上,即地址的低两位为bits[0b0],也就是说地址必须是2的倍数。 遵循以上方式叫对齐(aligned)方式,不遵守这样方式称为非对齐(unaligned)的存储访问操作。五、ARM平台字节对齐关键字1. __align(num)用于修改最高级别对象的字节边界。 A、在汇编中使用LDRD或者STRD时,就用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。 B、该修饰对象的命令最大是8个字节限制,可让2字节的对象按4字节对齐,但不能让4字节的对象2字节对齐。 C、 __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。2. __packed 是进行一字节对齐。 A、不能对packed的对象进行对齐; B、所有对象的读写访问都进行非对齐访问; C、float及包含float的结构联合及未用__packed的对象将不能字节对齐; D、__packed对局部整形变量无影响; E、强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为: packed __packed int* //__packed int 则没有意义。 3. __unaligned 用于修饰该变量可按照非对齐访问。六、如何查找与字节对齐方面的问题,如果出现对齐或者赋值问题首先查看: 1. 编译器的big little端设置; 2. 看这种体系本身是否支持非对齐访问; 3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。七、结论 针对于32位处理器对于本地使用的数据结构,为提高内存访问效率,采用四字节对齐方式;同时为了减少内存的开销,合理安排结构成员的位置,减少四字节对齐导致的成员之间的空隙,降低内存开销。 对于处理器之间的数据结构,需要保证消息的长度不因为在不同编译平台和不同处理器导致消息结构的长度发生变化,使用一字节对齐方式对消息结构进行紧缩;为保证处理器之间的消息的数据结构的内存访问效率,采用字节填充的方式自己对消息中成员进行四字节对齐。 数据结构的成员位置要兼顾成员之间的关系、数据访问效率和空间利用率。顺序安排的原则是:四字节的放在最前面,两字节的紧接最后一个四字节成员,一字节紧接最后一个两字节成员,填充字节放在最后。举例如下:typedef struct tag_T_MSG{long ParaA;long ParaB;short ParaC;char ParaD;char P} T_MSG;
分享给小伙伴们:
我来说两句……
最新技术贴
微信公众号二
微信公众号一博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)老问题:内存对齐。
[问题点数:20分]
老问题:内存对齐。
[问题点数:20分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|}

我要回帖

更多关于 c语言内存对齐 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信