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 ...
随机推荐
- windows下Composer因php_openssl扩展缺失而安装失败
Composer(https://getcomposer.org/)是PHP下的一个依赖管理工具.你可以在你的项目中声明你所需要用到的类库,然后Composer会在项目中为你安装它们.如果你了解Nod ...
- C 标准库 - string.h之memmove使用
memmove Move block of memory Copies the values of num bytes from the location pointed by source to t ...
- module使用和设置
Modules environmentDescription This is a system that allows you to easily change between different v ...
- Flex4 初始化过慢解决方法
昨天找了个免费.net空间,想测试一下做的一个简单Flex4 上传项目的效果.上传所有文件到网站之后,访问项目页面,进度条一点一点艰难的向前移动,到了100%后却不出现程序界面,等待一会儿还是没有出现 ...
- bootstrap中对dropdown使用hover代替click
bootstrap的下拉组件需要点击click才能展示下拉列表,这在使用导航的时候很不方便因此有一个扩展的组件来解决这个问题. 在VS的Nuget中查询bootstrap-hover-dropdown ...
- weblogic 异常常见处理方法
1.系统日志 通过分析weblogic的系统日志及应用程序出错的地方,可能找到异常的原因. 2. 数据库连接 如果用户访问系统过慢,且连接池已经占满而weblogic的线程数量很少,就要查看应用是否没 ...
- 周记7——ios中picker滑动穿透bug
Bug描述:使用mint-ui的picker组件时,datepicker和picker在ios的webview(bug是在Hybrid App发现的)中会出现滑动穿透的现象,导致弹层后面的页面也会滚动 ...
- Nginx 基于客户端 IP 来开启/关闭认证
前些日子帮助公司在搭建了一个内部资源的导航页面,方便公司员工访问各种常用的系统.因为这个页面包含一些敏感信息,我们希望对其做认证,但仅当从外网访问的时候才开启,当从公司内网访问的时候,则无需输入账号密 ...
- 手把手教你用 Keras 实现 LSTM 预测英语单词发音
1. 动机 我近期在研究一个 NLP 项目,根据项目的要求,需要能够通过设计算法和模型处理单词的音节 (Syllables),并对那些没有在词典中出现的单词找到其在词典中对应的押韵词(注:这类单词类似 ...
- nvm安装最新稳定版node
安装当前最新的稳定版. nvm install stable