原文转自:http://www.cppblog.com/snailcong/archive/2009/03/16/76705.html

首先由一个程序引入话题:

//环境:vc6 + windows sp2

//程序1

#include <iostream>
using namespace std;
struct st1
{
char a ;
int  b ;
short c ;
};
struct st2
{
short c ;
char  a ;
int   b ;
};
int main()
{
cout<<"sizeof(st1) is "<<sizeof(st1)<<endl;
cout<<"sizeof(st2) is "<<sizeof(st2)<<endl;
return 0 ;
}
程序的输出结果为:
sizeof(st1) is 12
sizeof(st2) is 8

问题出来了,这两个一样的结构体,为什么sizeof的时候大小不一样呢?

本文的主要目的就是解释明白这一问题。

内存对齐,正是因为内存对齐的影响,导致结果不同。

对于大多数的程序员来说,内存对齐基本上是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体大小的不同。

那么编译器为什么要进行内存对齐呢?程序1中结构体按常理来理解sizeof(st1)和sizeof(st2)结果都应该是7,4(int) + 2(short) + 1(char) = 7 。经过内存对齐后,结构体的空间反而增大了。

在解释内存对齐的作用前,先来看下内存对齐的规则:

<!--[if !supportLists]-->1、  <!--[endif]-->对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。

<!--[if !supportLists]-->2、  <!--[endif]-->在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

#pragma pack(n) 表示设置为n字节对齐。 VC6默认8字节对齐

以程序1为例解释对齐的规则 :

St1 :char占一个字节,起始偏移为0 ,int 占4个字节,min(#pragma pack()指定的数,这个数据成员的自身长度) = 4(VC6默认8字节对齐),所以int按4字节对齐,起始偏移必须为4的倍数,所以起始偏移为4,在char后编译器会添加3个字节的额外字节,不存放任意数据。short占2个字节,按2字节对齐,起始偏移为8,正好是2的倍数,无须添加额外字节。到此规则1的数据成员对齐结束,此时的内存状态为:

oxxx|oooo|oo

0123 4567 89 (地址)

(x表示额外添加的字节)

共占10个字节。还要继续进行结构本身的对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,st1结构中最大数据成员长度为int,占4字节,而默认的#pragma pack 指定的值为8,所以结果本身按照4字节对齐,结构总大小必须为4的倍数,需添加2个额外字节使结构的总大小为12 。此时的内存状态为:

oxxx|oooo|ooxx

0123 4567 89ab  (地址)

到此内存对齐结束。St1占用了12个字节而非7个字节。

St2 的对齐方法和st1相同,读者可自己完成。

内存对齐的主要作用是:

<!--[if !supportLists]-->1、  <!--[endif]-->平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

<!--[if !supportLists]-->2、  <!--[endif]-->性能原因:经过内存对齐后,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遇到未对齐边界就直接罢工了。

图片来自:Data alignment: Straighten up and fly right

如大家对内存对齐对性能的具体影响情况,可以参考上文。

内存对齐的规则以及作用 verygood!的一篇文章!相关推荐

  1. 内存对齐的规则以及作用

    内存对齐能够用一句话来概括: "数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上" 比如int类型占用4个字节.地址仅仅能在0,4.8等位置上. 由一个程序引入话题: //环 ...

  2. 内存对齐的原理,作用,例子以及一些规划(中英文说明,适用sizeof结构体) ...

    目录   题记  一  内存读取粒度   Memory access granularity   从内存的角度解释内存对齐的原理   队列原理 Alignment fundamentals   Laz ...

  3. c++内存对齐的规则

    内存对齐规则: 默认条件下,内存对齐是以class/struct中最大的那个基本类型为基准的(x倍数) 如果在类的前面有 #pragma pack(n) min(#pragma pack(n), 最大 ...

  4. 内存对齐是什么?为什么要内存对齐?

    原文: http://blog.csdn.net/liupeng900605/article/details/7530010 首先由一个程序引入话题:  1 //环境:vc6 + windows sp ...

  5. 什么是内存对齐?如何计算内存对齐?为什么要内存对齐?

    内存对齐概念: 要了解内存对齐,我们可以先看下面的例子: typedef struct S {int a;double b;char c; }S;typedef struct B {int a;cha ...

  6. 【C语言】自定义类型(结构体、位段、枚举、联合体)与内存对齐

    目录 一.自定义类型种类 1.结构体 2.位段 3.枚举 4.联合体(共同体) 二.结构体 1.结构体的声明 2.结构体变量的定义与初始化 1.定义 2.结构体变量的初始化 3.结构体成员的访问 1. ...

  7. Windows内存体系(6) -- 彻底理解内存对齐

    一.内存为什么要对齐 虽然所有的变量都是保存在特定地址的内存中,但最好还是按照内存对齐的要求来存储.这主要出于两个方面的原因考虑: 平台原因: 不是所有的硬件平台(特别是嵌入式系统中使用的低端处理器) ...

  8. C++ 中内存对齐原理及作用

    struct/class/union内存对齐原则有四个: 1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储 ...

  9. 内存对齐规则及为什么内存对齐

    内存对齐的默认规则: (1)第一个成员在结构体变量偏移量为0的地址处 (2)从第二个成员开始,每个成员都有对齐数,对齐数是其所占字节的整数倍 (3)结构体的总大小为其成员的最大对齐数的整数倍 stru ...

最新文章

  1. 根据坐标如何标记图片_如何玩转FloodFill算法?
  2. 草根seo站长利用网站赚钱的方法
  3. cygwin nginx php mysql_Windows 下 Nginx + PHP5 的安装与配置
  4. 用坐标实现物体的移动c语言,C语言之实现控制台光标随意移动的实例代码
  5. C语言经典算法100例(二)
  6. QT的QLocale类的使用
  7. java多个数据库数据进行访问_通过Spring Boot配置动态数据源访问多个数据库的实现代码...
  8. Linux中ping不通外网
  9. 新闻视频 36:整合首页 用到 Repeater 主要用gridview /gridview去掉边框用到 BorderWidth=”0” inner join和 left...
  10. Oracle分析函数-rank() over(partition by...order by...)
  11. MangoFix:iOS热修复另辟蹊径
  12. 查看linux可用磁盘空间_如何在Linux中检查可用磁盘空间
  13. java面试中mysql_面试官:Mysql 中主库跑太快,从库追不上怎么整?
  14. WIN8系统中 任务管理器 性能栏 显示CPU利用率(已暂停)怎么回事?
  15. 【数值分析】拉格朗日插值与牛顿插值
  16. JDK ThreadLocal解析
  17. Will----个人资料
  18. 一个关于数论中拉格朗日定理的证明
  19. 从影片《点球成金》看大数据如何创造商业奇迹!
  20. 【Kotiln】 错误锦集- Caused by: kotlin.NotImplementedError: An operation is not implemented: not implemen

热门文章

  1. Photoshop CS2/CS5/CS6/CC2015/CC2018/CC2017/CC2019软件安装及按照包
  2. Cadence Allegro导出BOM清单图文教程及视频演示
  3. git 默认的名字和账号
  4. 如何提升自身能力?不再平庸
  5. 国防创新小组(DIU)选择Immervision InnovationLab为Blue UAS Framework项目开发计算机视觉广角摄相机
  6. Mac如何查看隐藏文件夹
  7. 函数与事件吧,未整理
  8. 万年历,可以查 曰、月、年,缓存到redis,直接抓取使用,
  9. 开发者必看 | DevWeekly 第1期:什么是时间复杂度?
  10. 8086/8088 寻址方式补充篇