我们的讨论从一道经典的题目开始:

Intel和微软同时出现的面试题

#pragma pack(8)

struct s1{
char a;
long b;
};

struct s2{
char c;
s1 d;
long long e;
};

#pragma pack()

问 
1.sizeof(s2) = ?
2.s2的c后面空了几个字节接着是d?

经上机测试,sizeof(s2)=24。s2的C后面空了7个字节。这里涉及到一个我们平时可能会忽视的问题——内存对齐【Memory alignment 】。

1.内存对齐定义: 
    现在使用的计算机中内存空间都是按照字节划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际上计算机系统对于基本数据类型在内存中的存放位置都有限制,要求这些数据存储首地址是某个数K的倍数,这样各种基本数据类型在内存冲就是按照一定的规则排列的,而不是一个紧挨着一个排放,这就是内存对齐。

对齐模数: 
    内存对齐中指定的对齐数值K成为对齐模数(Alignment Modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。

2.内存对齐的好处: 
    内存对齐作为一种强制的要求,第一简化了处理器与内存之间传输系统的设计,第二可以提升读取数据的速度。各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。 
    Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。但是如果想提升性能,应该注意内存对齐方式。 
ANSI C标准中并没有规定相邻声明的变量在内存中一定要相邻。为了程序的高效性,内存对齐问题由编译器自行灵活处理,这样导致相邻的变量之间可能会有一些填充字节。对于基本数据类型(int char等),他们占用的内存空间在一个确定硬件系统下有确定的值。ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和

3.从80X86结构看内存对齐问题

  3.1 8bit数据总线CPU内存对齐问题:

8088和80188的数据总线宽度为8bit,一个内存周期只能读取一个字节。每次读取内存都需要给地址总线提供需要读写内存单元的地址。因此,对于char类型数据, 读取只需要访问一次内存;对于int类型数据,读取需要4次访问内存;所以在这种情况下,没有内存对齐的说法。

3.2 16bit数据总线CPU内存对齐问题

8086,80186,80286和80386sx都是16bit数据总线的CPU,这样一个内存周期可以读取16bit数据也就是一个字(word),两个字节的数据。在这种CPU中,内存单元被分成了偶单元(even)和奇单元(odd),如下图所示:

图5 奇单元和偶单元

16bit数据总线中,0-7bit与偶单元相连,8-15bit与奇单元相连。如下图所示:

图6 16bit数据总线内存访问

每个内存周期,CPU只能读取一个偶单元和一个奇单元地址总线的地址为偶单元的地址,所以说地址总线的地址永远是2对齐的。

假如我们读取一个字(Word)数据,该数据是以2对齐的,则一个内存周期即可把它读出开。如果我们读取字(word)数据不是以2对齐的,那么需要读取两个字节周期来完成。比如读取193内存单元中的字,第一个周期中,CPU把地址192放入地址总线,读取其高字节——地址193(16bit数据总线CPU有字节模式)放入地址总线的8-15bit,第二个周期中,CPU把地址194放入地址总线,内存子系统把读取其低字节——地址194,并把其放入地址总线的0-7bit。注意这样一个未对齐字的读取方式不但效率不高,而且得不得想要的结果,CPU会自动的交换0-7bit到8-15bit。

对于读取一个字节,如果该字节的地址是2对齐的,那么CPU直接把该地址放入地址总线,读取该地址的数据,放入数据总线的0-7bit。如果该字节的地址不是以2对齐的,那么CPU把(该地址-1)的地址放入总线,并读取高字节,放入数据总线的8-15bit,最后CPU自动把8-15bit的数据,交换到0-7bit完成字节的读取。

对于双字(Double Word)的访问,如果以2对齐,则只需要2个周期即可完成。如果没有以2对齐,则需要3个内存周期来完成,具体原因可以参考字和字节的分析。

3.3 32bit数据总线的内存访问

数据总线和内存的连接如下图所示:

图7 32bit数据总线内存访问

通过上图和参考上面16bit数据总线CPU的分析,可以看出32bit数据总线在每个内存周期中读取的数据地址都是以4对齐的。

如果读取一个双字的地址是以4内存对齐的话,一个内存周期即可读出。如果不是以4对齐,则需要两个内存周期来完成操作。

如果读取一个字的地址对4取模余3的话,那么读取该字需要两个内存周期(CPU会自动完成高位和低位的交换)。如果读取一个字的地址对4取模不余3的话,则只需要一个内存周期即可完成读写。

对于字节,任何字节地址的访问都是一个内存周期。

4.下面我们回到C语言层面讨论这个问题:

4.1在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
     · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
     · 使用伪指令#pragma pack (),取消自定义字节对齐方式。

另外,还有如下的一种方式:
     · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
     · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。

4.2内存对齐策略:

     4.2.1 编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。

4.2.2 成员对齐,即每个成员分别对齐.即每个成员按自己的方式对齐.也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其  类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.(对于指定参数为8时,大于等于4字节的成员的起始位置应该处在4字节的整数倍上,对于2字节成员的起始位置应该处在2字节的整数倍上,1字节的起始位置任意)

4.2.3结构的长度必须为所用过最大对齐参数的整数倍,不够就补空字节.

程序示例1:

#pragma pack(2)

struct s1{

char a;

long b;

};

cout<<sizeof(s1)<<endl;

结果:6

程序示例2:

#pragma pack(8)

struct s1{

char a;

long b;

};

cout<<sizeof(s1)<<endl;

结果:8

转载于:https://www.cnblogs.com/liujiangyi/archive/2011/08/22/2149660.html

关于内存对齐问题的一些资料整理相关推荐

  1. 结构体的内存对齐规则

    1.结构体的内存对齐规则 1.第一个成员在与结构体变量偏移量为0的地址处. 2.其他成员变量都放在对齐数(成员的大小和默认对齐数的较小值)的整数倍的地址处. 对齐数=编译器默认的一个对齐数与该成员大小 ...

  2. 2万字 + 50 张图,细说 JVM 内存分布、内存对齐、压缩指针

    今天为大家带来一篇 2 万字的硬核技术文章. 本文我们将从计算机组成原理的角度详细阐述对象在JVM内存中是如何布局的,以及什么是内存对齐,如果我们头比较铁,就是不进行内存对齐会造成什么样的后果,最后引 ...

  3. 一图看懂 docx 读取、查询、修改 Ms Word docx 文件, 资料整理+笔记(大全)

    本文由 大侠(AhcaoZhu)原创,转载请声明. 链接: https://blog.csdn.net/Ahcao2008 一图看懂 docx 读取.查询.修改 Ms Word docx 文件, 资料 ...

  4. 内存对齐与ANSI C中struct型数据的内存布局 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3032209.html 当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将 ...

  5. struc,union,class的内存对齐方式

    首先思考一个问题:int,short,char的struct,这几个数应该怎么放,内存最小 strct ts { int a; short b; char c; }; 这样放最小,为8,这样放置,只会 ...

  6. 计算机大端模式和小端模式 内存对齐问题(sizeof)

    目录(?)[+] 一大端模式和小端模式的起源 二什么是大端和小端 三数组在大端小端情况下的存储 四为什么会有大小端模式之分呢 五如何判断机器的字节序 内存对齐问题 再讲讲pragma pack 内存对 ...

  7. GNU C - 关于8086的内存访问机制以及内存对齐(memory alignment)

    接着前面的文章,这篇文章就来说说menory alignment -- 内存对齐. 一.为什么需要内存对齐? 无论做什么事情,我都习惯性的问自己:为什么我要去做这件事情? 是啊,这可能也是个大家都会去 ...

  8. C语言:--位域和内存对齐

    位域 位域是指信息在保存时,并不需要占用一个完整的字节,而只需要占几个或一个二进制位.为了节省空间,C语言提供了一种数据结构,叫"位域"或"位段". " ...

  9. 软件设计师提纲+复习资料整理(上午题)

    文章目录 软件设计师考试大纲 上午题(选择题) 一.计算机组成原理 考点:CPU结构组成 考点:原码.反码.补码定点整数范围 考点:浮点数表示 考点:RISC和CISC计算机的区别 考点:奇校验与偶校 ...

最新文章

  1. ASP.NET MVC3细嚼慢咽---(2)模板页
  2. 阿里推出 PolarFS 分布式文件系统:将存储与计算分开(附论文)
  3. java 命名管道_利用Windows命名管道实现IPC的一种有效方法
  4. 【自动驾驶】3. DDS 数据分发服务(Data Distribution Service)
  5. activemq配置与启动
  6. Hadoop sqoop从MySQL导入数据到HDFS
  7. 81.游戏项目-物体任意角度飞行和停止
  8. UnpooledDirectByteBuf源码分析
  9. codeforces1467 E. Distinctive Roots in a Tree(树上差分)
  10. 一套通用的后台管理系统Base Admin 前端:layui 后端:java
  11. 微信小程序测试版部署
  12. db9针232接口波特率标准_RS232 DB9 计算机接口定义
  13. 计算机基础(01)基础知识
  14. Java SE 正则表达式
  15. 银行核心业务系统性能测试方法
  16. 产品冷思考:大而全or小而美如何选择?
  17. Vue框架+Axios框架
  18. java 正序a~z_java 策略模式,list集合,实现id 姓名年龄正序倒序排序(如果年龄或者姓名重复,按id正序排序)...
  19. (二维树状数组)E - Stars
  20. 一个好的web前端开发者,是怎么学习的?前端开发培训机构哪个比较好

热门文章

  1. CVPR 2021 机器学习及多模态最新进展分享
  2. 应用机器学习视频教程,哥伦比亚大学 2020版
  3. AI算力霸主诞生!英伟达发布首款安培架构GPU,性能提升20倍
  4. fiddler汉化版可以改成英文吗_可以把推拉门改成平开窗吗?推拉门和平开窗哪个更好?...
  5. 开源 | 高颜值神经网络可视化工具
  6. 公布 | 中国图象图形学学会首批Fellow名单公布
  7. 【Python】Python实战从入门到精通之七 -- 教你深入理解异常处理
  8. 精选论文集|Transformer在视觉领域中的应用
  9. 数据结构(五)堆排序
  10. 自动驾驶路径规划论文解析(5)