C/C++内存对齐详解
1、什么是内存对齐
还是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,char占一个byte,那么将它们放到一个结构体中应该占4+1=5byte;但是实际上,通过运行程序得到的结果是8 byte,这就是内存对齐所导致的。
//32位系统
#include<stdio.h>
struct{
int x;
char y;
}s; int main()
{
printf("%d\n",sizeof(s); // 输出8
return 0;
}
现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐。
2、为什么要进行内存对齐
尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.
现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。
假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的连续四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.
简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错)。
现在有了内存对齐的,int类型数据只能存放在按照对齐规则的内存中,比如说0地址开始的内存。那么现在该处理器在取数据时一次性就能将数据读出来了,而且不需要做额外的操作,提高了效率。
3、内存对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。gcc中默认#pragma pack(4),可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这一系数。
有效对其值:是给定值#pragma pack(n)和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位。
了解了上面的概念后,我们现在可以来看看内存对齐需要遵循的规则:
(1) 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
(3) 结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
下面给出几个例子以便于理解:
//32位系统
#include<stdio.h>
struct
{
int i;
char c1;
char c2;
}x1; struct{
char c1;
int i;
char c2;
}x2; struct{
char c1;
char c2;
int i;
}x3; int main()
{
printf("%d\n",sizeof(x1)); // 输出8
printf("%d\n",sizeof(x2)); // 输出12
printf("%d\n",sizeof(x3)); // 输出8
return 0;
}
以上测试都是在Linux环境下进行的,linux下默认#pragma pack(4),且结构体中最长的数据类型为4个字节,所以有效对齐单位为4字节,下面根据上面所说的规则以s2来分析其内存布局:
首先使用规则1,对成员变量进行对齐:
sizeof(c1) = 1 <= 4(有效对齐位),按照1字节对齐,占用第0单元;
sizeof(i) = 4 <= 4(有效对齐位),相对于结构体首地址的偏移要为4的倍数,占用第4,5,6,7单元;
sizeof(c2) = 1 <= 4(有效对齐位),相对于结构体首地址的偏移要为1的倍数,占用第8单元;
然后使用规则2,对结构体整体进行对齐:
s2中变量i占用内存最大占4字节,而有效对齐单位也为4字节,两者较小值就是4字节。因此整体也是按照4字节对齐。由规则1得到s2占9个字节,此处再按照规则2进行整体的4字节对齐,所以整个结构体占用12个字节。
根据上面的分析,不难得出上面例子三个结构体的内存布局如下:
#pragma pack(n)
不同平台上编译器的 pragma pack 默认值不同。而我们可以通过预编译命令#pragma pack(n), n= 1,2,4,8,16来改变对齐系数。
例如,对于上个例子的三个结构体,如果前面加上#pragma pack(1),那么此时有效对齐值为1字节,此时根据对齐规则,不难看出成员是连续存放的,三个结构体的大小都是6字节。
如果前面加上#pragma pack(2),有效对齐值为2字节,此时根据对齐规则,三个结构体的大小应为6,8,6。内存分布图如下:
经过上面的实例分析,大家应该对内存对齐有了全面的认识和了解,在以后的编码中定义结构体时需要考虑成员变量定义的先后顺序了。
参考资料:
http://light3moon.com/2015/01/19/[%E8%BD%AC]%20%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/
C/C++内存对齐详解的更多相关文章
- C语言内存对齐详解(2)
接上一篇:C语言内存对齐详解(1) VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式.VC 中提供了#pr ...
- C语言内存对齐详解(3)
接上一篇:C语言内存对齐详解(2) 在minix的stdarg.h文件中,定义了如下一个宏: /* Amount of space required in an argument list for a ...
- C语言内存对齐详解
一.字节对齐基本概念 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型 ...
- [转]C++结构体|类 内存对齐详解
内存地址对齐,是一种在计算机内存中排列数据(表现为变量的地址).访问数据(表现为CPU读取数据)的一种方式,包含了两种相互独立又相互关联的部分:基本数据对齐和结构体数据对齐 . 为什么需要内存对齐?对 ...
- C语言内存对齐详解(1)
一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...
- 3.c语言结构体成员内存对齐详解
一.关键一点 最关键的一点:结构体在内存中是一个矩形,而不是一个不规则形状 二.编程实战 #include <stdlib.h> #include <stdio.h> stru ...
- 【转载】图说C++对象模型:对象内存布局详解
原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在 ...
- Tomcat内存设置详解
Java内存溢出详解 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError: Java heap space ----JVM Heap(堆)溢出 JVM在 ...
- Tomcat内存溢出详解【转载】
本文转载自 http://elf8848.iteye.com/blog/378805 Java内存溢出详解 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryEr ...
随机推荐
- Bootstrap 的基本使用
一.Bootstrap简介 Bootstrap 是目前受欢迎的前端框架之一,是基于HTML,CSS,JavaScript的,它简洁灵活,使web开发更加快捷 中文官网:http://www.bootc ...
- oracle 11g调优常用语句
1.查询表的基数及选择性 select a.column_name, b.num_rows, a.num_distinct cardinality, round( ...
- Linux下MySQL数据库的备份与恢复
Linux下MySQL数据库的备份与恢复 作者:Grey 原文地址: Github 语雀 博客园 基于版本 MySQL5.7 Deepin Linux 15.11 xtrabackup-2.4.18 ...
- Windows 64位下安装php的redis扩展(php7.2+redis3.0)
前置条件:为php7.2搭建redis扩展的前提是在本机上已经成功搭建好php的运行环境,我的电脑的运行环境时 apache2.4+mysql5.5+php7.2. 操作系统为64位,编译环境为Mic ...
- 【软件测试 Python自动化】全网最全大厂面试题,看完以后你就是面试官!
前言 为了让大家更好的理解和学习投入到Python自动化来找到一份好的资料也是学习过程中,非常重要的一个点.你的检索能力越强,你就会越容易找到最合适你的资料. 有需要的小伙伴可以复制群号 313782 ...
- C#中无法找到microsoft.web文件
原因 上午更新了vs2015后,找不到该文件. 解决方法 1.工具->Nuget包管理器->程序包管理器控制台-> 输入 Install-Package Microsoft.Web. ...
- 搭建一个简单的springMVC框架
//新建一个简单的maven项目,选择war包 //web.xml配置 <?xml version="1.0" encoding="UTF-8"?> ...
- Class的一些使用技巧?
1.forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象.例如 Object obj = Class.forName(s).newInstance(); 2.虚拟机 ...
- JS验证三种提示框
1 <form> 2 <!-- 按钮选择点击 --> 3 <tr> 4 <td><input type="button" na ...
- 高效实用linux命令之-history
History(历史)命令用法 15 例 如果你经常使用 Linux 命令行,那么使用 history(历史)命令可以有效地提升你的效率.本文将通过实例的方式向你介绍 history 命令的 15 个 ...