C/C++内存对齐 ZZ
这篇文章写得非常深入浅出。推荐。图需要到原博看。
http://songlee24.github.io/2014/09/20/memory-alignment/
下面是网易的一道笔试题:struct { uint32_t m1; char m2; } varray[2];以下哪些判断一定成立?(多选)
sizeof(varray[0]) == 5sizeof(varray[0]) == 8(void*)&(varray[0].m1) < (void*)&(varray[0].m2)(char*)&varray[0] == (char*)&(varray[0].m1)(char*)&varray[0] + sizeof(varray[0]) == (char*)&varray[1](char*)&(varray[0].m2) + 1 == (char*)&varray[1](char*)&(varray[0].m2) + 4 == (char*)&varray[1]
这个题目考查的就是内存对齐的知识点,看完这篇文章你就知道这道题应该选什么了。
一、什么是内存对齐
内存对齐(Memory alignment),也叫字节对齐。
现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
举一个简单的例子,uint32_t所占内存空间为 4 bytes,char为 1 byte。如果把它们放在一个结构体中,则所占的内存空间应该是 4 + 1 = 5 bytes 。而事实上,在VS2012和gcc环境下,sizeof 操作的结果都是 8 bytes:
1 |
struct |
示图:

这里是以4个字节为一个对齐单位。
二、为什么要内存对齐
之所以要内存对齐,有两方面的原因:
平台原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。————- 比如,有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐。
性能原因:内存对齐可以提高存取效率。————- 比如,有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。
三、对齐的规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。你可以通过预编译命令#pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中 n 就是你要指定的“对齐系数”。
1)概念:
有效对齐值:是 #pragma pack指定值 和 结构体中最长数据类型长度 中较小的那个。有效对齐值也叫对齐单位。
注意:VS、VC 默认是#pragma pack(8),而 gcc 默认是#pragma pack(4),并且gcc只支持1,2,4对齐。
2)规则:
结构体变量的首地址是有效对齐值(对齐单位)的整数倍。
结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
结构体内类型相同的连续元素将在连续的空间内,和数组一样。
下面给出几个例子帮助理解(测试环境为VS2012):
例一:
1 |
struct |
可以看出,上面定义的三个结构体只是交换了成员声明的顺序。由于结构体中最长的数据类型为4个字节,而VS2010默认#pragma pack(8),所以有效对齐值(对齐单位)为 4 bytes。根据前三条规则可以画出以下图:
.png)
例二:
1 |
struct |
上面结构体中最长的数据类型 double 为 8 个字节,而VS2012中默认#pragma pack(8),所以有效对齐值(对齐单位)为 8 bytes。根据前三条规则可以画出以下图:
.png)
其中的字符数组 char b[6] 可以看做6个单独的 char 成员。
四、pragma pack(n)
上面说到,不同平台上编译器的 pragma pack 默认值不同。而我们可以通过预编译命令
#pragma pack(n),n=1,2,4,8,16 来改变这一对齐系数。#pragma pack(n)是通过改变有效对齐值来改变数据成员在内存中的布局,若你设定的 n 值没有影响或改变有效对齐值,则成员在内存中的布局不会改变。
下面就看看在1、2、4字节对齐的情况下例一、例二的变化:
1字节对齐:#pragma pack(1)
这时的有效对齐值(对齐单位)为 1 字节,则根据对齐规则,可知成员都是连续存储的。
例一中的输出结果会变为 6,6,6,如下图:
.png)
例二中输出结果则变为 4 + 6 + 8 = 18:
.png)
2字节对齐:#pragma pack(2)
这时的有效对齐值(对齐单位)为 2 字节,则根据对齐规则,可知例一的输出结果会变为 6,8,6,如下图:
.png)
例二结构体中最长数据类型 double 为 8 个字节,所以有效对齐值是 2 。此时输出结果还是18,如下图:
.png)
4字节对齐:#pragma pack(4)
对于例一,结构体中最长的数据类型 int 是 4 个字节,所以此时的有效对齐值(对齐单位)仍为 4,没有变化,所以输出仍然是 8,12,8。
而在例二中,原来的有效对齐值为 8,现在变成了 4 。所以输出结果变为 20,具体如下图:
.png)
对于 8 字节对齐、 16 字节对齐,在这里就不举例了,相信根据对齐规则你可以很容易写出来。需要注意的是,有些编译器,比如gcc,只支持 1,2,4 对齐。
附:答案
相信看到这里,文章开头的那个网易笔试题应该就很容易得出答案了。只需要根据内存对齐把结构体的内存布局图画出来就一目了然了:
.png)
所以多选答案应该是 2、4、5、7。
对于这种类型的题目,最好的办法就是根据对齐规则画出对齐后的内存布局图,简单清晰且不容易出错。
C/C++内存对齐 ZZ的更多相关文章
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
- C/C++: C++位域和内存对齐问题
1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...
- C/C++ 知识点1:内存对齐
预备知识:基本类型占用字节 在32位操作系统和64位操作系统上,基本数据类型分别占多少字节呢? 32位操作系统: char : 1 int :4 short : 2 unsigned ...
- Windows+GCC下内存对齐的常见问题
结构/类对齐的声明方式 gcc和windows对于modifier/attribute的支持其实是差不多的.比如在gcc的例子中,内存对齐要写成: class X { //... } __attrib ...
- c++内存对齐
内存对齐原则: 1.数据成员对齐规则:struct, union的数据成员,第一个数据成员放在offset为0的地方,之后的数据成员的存储起始位置都是放在该数据成员大小的整数倍位置.如在32bit的机 ...
- C语言中内存对齐
今天一考研同学问我一个问题,一个结构体有一个int类型成员和一个char类型成员,问我这个结构体类型占多少个字节,我直接编个程序给他看结果.这个结构体占八个字节,咦,当时我蛮纳闷的,一个int类型四个 ...
- 内存对齐 和 sizeof小结
数据对齐(内存对齐)指该数据所在的地址必须是该数据长度的整数倍.X86CPU能直接访问对齐的数据,当它试图访问未对齐的数据时,会在内部进行一系列的调整,降低运行速度.数据对齐一般出现在结构体和类中,在 ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
- C语言再学习之内存对齐
昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...
随机推荐
- 连接Git@OSC操作步骤
一.准备工作 软件下载 Git:地址 TortoiseGit:地址 二.安装与配置 1.Git安装 Git配置 设置客户端的用户名和email 然后,到Git@OSC 上面注册一个帐号. 这个帐号就是 ...
- iframe 解析
简介:iframe在日常的开发中经常用到,本随笔在参考http://blog.csdn.net/cuew1987/article/details/11265153的情况下,将对iframe的常用用法进 ...
- TortoiseGit学习系列之TortoiseGit基本操作拉取项目(图文详解)
前面博客 TortoiseGit学习系列之TortoiseGit基本操作克隆项目(图文详解) TortoiseGit学习系列之TortoiseGit基本操作修改提交项目(图文详解) TortoiseG ...
- Linux下Tomcat8.0.44配置使用Apr
听说Apr可以提高tomcat很多的性能,配置具体如下 1.安装apr 1.5.2 [root@ecs-3c46 ]# cd /usr/local/src [root@ecs-3c46 src]# w ...
- 微服务Kong(五)——添加一个用户(Consumer)
在本节中,我们将学习如何添加一个用户(consumer)到KONG实例中.用户是与使用您的API的个人相关联,可用于跟踪,访问管理等. NOTE:本节假设您已经正确启用了密钥验证插件.如果没有,请参考 ...
- mysql 索引数据结构及原理
原文:http://www.uml.org.cn/sjjm/201107145.asp 1 索引的本质 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构.提取句子 ...
- ViewPager(视图滑动切换工具)
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.Cons ...
- 四、cent OS安装配置mysql
下载mysql的repo源wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 安装mysql-community-r ...
- HDU 3306 Another kind of Fibonacci ---构造矩阵***
Another kind of Fibonacci Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- linux-ubuntu安装配置uwsgi
参考原文 对于 Python2.x 版本:(测试通过) 第一步:sudo apt-get install python-dev 第二步:sudo apt-get install python-pip ...