转载和积累系列 - 内存对齐

转载 2015年07月06日 21:24:10

1. 内存对齐(Data Structure Alignment)是什么

内存对齐,或者说字节对齐,是一个数据类型所能存放的内存地址的属性(Alignment is a property of a memory address)。
这个属性是一个无符号整数,并且这个整数必须是2的N次方(1、2、4、8、……、1024、……)。
当我们说,一个数据类型的内存对齐为8时,意思就是指这个数据类型所定义出来的所有变量,其内存地址都是8的倍数。

当一个基本数据类型(fundamental types)的对齐属性,和这个数据类型的大小相等时,这种对齐方式称作自然对齐(naturally aligned)。
比如,一个4字节大小的int型数据,默认情况下它的字节对齐也是4。

2. 为什么我们需要内存对齐


这是因为,并不是每一个硬件平台都能够随便访问任意位置的内存的。

微软的MSDN里有这样一段话

Many CPUs, such as those based on Alpha, IA-64, MIPS, and SuperH architectures, refuse to read misaligned data. When a program requests that one of these CPUs access data that is not aligned, the CPU enters an exception state and notifies the software that it cannot continue. On ARM, MIPS, and SH device platforms, for example, the operating system default is to give the application an exception notification when a misaligned access is requested.

大意是说,有不少平台的CPU,比如Alpha、IA-64、MIPS还有SuperH架构,若读取的数据是未对齐的(比如一个4字节的int在一个奇数内存地址上),将拒绝访问,或抛出硬件异常。

另外,在维基百科里也记载着如下内容

Data alignment means putting the data at a memory offset equal to some multiple of the word size, which increases the system's performance due to the way the CPU handles memory.

意思是,考虑到CPU处理内存的方式(32位的x86 CPU,一个时钟周期可以读取4个连续的内存单元,即4字节),使用字节对齐将会提高系统的性能(也就是CPU读取内存数据的效率。比如你一个int放在奇数内存位置上,想把这4个字节读出来,32位CPU就需要两次。但对齐之后一次就可以了)。

3. 内存对齐带来的数据结构大小变化

因为有了内存对齐,因此数据在内存里的存放就不是紧挨着的,而是可能会出现一些空隙(Data Structure Padding,也就是用于填充的空白内容)。因此对基本数据类型来说可能还好说,对于一个内部有多个基本类型的结构体(struct)或类而言,sizeof的结果往往和想象中不大一样。

让我们来看一个例子:

struct MyStruct
{
    char a;         // 1 byte
    int b;          // 4 bytes
    short c;        // 2 bytes
    long long d;    // 8 bytes
    char e;         // 1 byte
};

我们可以看到,MyStruct中有5个成员,如果直接相加的话大小应该是16,但在32位MSVC里它的sizeof结果是32。
之所以结果出现偏差,为了保证这个结构体里的每个成员都应该在它对齐了的内存位置上,而在某些位置插入了Padding。

下面我们尝试考虑内存对齐,来计算一下这个结构体的大小。首先,我们可以假设MyStruct的整体偏移从0x00开始,这样就可以暂时忽略MyStruct本身的对齐。这时,结构体的整体内存分布如下图所示:

我们可以看到,char和int之间;short和long long之间,为了保证成员各自的对齐属性,分别插入了一些Padding。
因此整个结构体会被填充得看起来像这样:

struct MyStruct
{
    char a;         // 1 byte
    char pad_0[3];  // Padding 3
    int b;          // 4 bytes
    short c;        // 2 bytes
    char pad_1[6];  // Padding 6
    long long d;    // 8 bytes
    char e;         // 1 byte
    char pad_2[7];  // Padding 7
};

注意到上面加了Padding的示意结构体里,e的后面还跟了7个字节的填充。这是因为结构体的整体大小必须可被对齐值整除,所以“char e”的后面还会被继续填充7个字节好让结构体的整体大小是8的倍数32。

我们可以在gcc + 32位linux中尝试计算sizeof(MyStruct),得到的结果是24。
这是因为gcc中的对齐规则和MSVC不一样,不同的平台下会使用不同的默认对齐值(The default alignment is fixed for a particular target ABI)。在gcc + 32位linux中,大小超过4字节的基本类型仍然按4字节对齐。因此MyStruct的内存布局这时看起来应该像这个样子:

下面我们来确定这个结构体类型本身的内存对齐是多少。为了保证结构体内的每个成员都能够放在它自然对齐的位置上,对这个结构体本身来说最理想的内存对齐数值应该是结构体里内存对齐数值最大的成员的内存对齐数。
也就是说,对于上面的MyStruct,结构体类型本身的内存对齐应该是8。并且,当我们强制对齐方式小于8时,比如设置MyStruct对齐为2,那么其内部成员的对齐也将被强制不能超过2。

为什么?因为对于一个数据类型来说,其内部成员的位置应该是相对固定的。假如上面这个结构体整体按1或者2字节对齐,而成员却按照各自的方式自然对齐,就有可能出现成员的相对偏移量随内存位置而改变的问题。
比如说,我们可以画一下整个结构体按1字节对齐,并且结构体内的每个成员按自然位置对齐的内存布局:

上面的第一种情况,假设MyStruct的起始地址是0x01(因为结构体本身的偏移按1字节对齐),那么char和int之间将会被填充2个字节的Padding,以保证int的对齐还是4字节。
如果第二次分配MyStruct的内存时起始地址变为0x03,由于int还是4字节对齐,则char和int之间将不会填充Padding(填充了反而不对齐了)。
以此类推,若MyStruct按1字节对齐时不强制所有成员的对齐均不超过1的话,这个结构体里的相对偏移方式一共有4种。

因此对于结构体来说,默认的对齐将等于其中对齐最大的成员的对齐值。并且,当我们限定结构体的内存对齐时,同时也限定了结构体内所有成员的内存对齐不能超过结构体本身的内存对齐。


内存对齐与内存分配原则

首先讲一个概念—-内存对齐一种提高内存访问速度的策略,cpu在访问未对其的内存需要经过两次内存访问,而经过内存对齐一次就可以了。(?)打个比方就是:操作系统在访问内存时,每次读取一定的长度(这个长度是...
  • tingyun_say
  • tingyun_say
  • 2016年05月18日 14:30
  • 2063

五分钟搞定内存对齐。

写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧.   /*******************...
  • me4weizhen
  • me4weizhen
  • 2016年09月19日 23:53
  • 961

C/C++ 内存对齐原则及作用

struct/class/union内存对齐原则有四个: 1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成...
  • chy19911123
  • chy19911123
  • 2015年10月04日 09:50
  • 3936

为什么要内存对齐

当我们听到”内存对齐“这个概念时,从字面意思来看,很容易理解。那就是让内存对齐。        当然  就会有人说  你这不是废话 ??         现在我就来说一说为什么要内存对齐以及怎么个对齐...
  • l_tudou
  • l_tudou
  • 2016年07月22日 23:40
  • 586

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

原文: http://blog.csdn.net/liupeng900605/article/details/7530010
  • chj90220
  • chj90220
  • 2014年10月10日 10:20
  • 1270

内存对齐以及如何关闭内存对齐

内存对齐以前接触过,知道有这么回事,昨天面试,面试官问了一个结构体内存分配相关的问题: struct _A{ int a; int b; char c; }A; sizeof(A)=?(32位机器) ...
  • a1205137569
  • a1205137569
  • 2015年10月08日 09:28
  • 352

对内存对齐的深一步理解

接触内存对齐这个概念,也有三四年了。不过由于我工作后一直做游戏服务器,都是在x86架构的机子上写代码,也没怎么注意内存对齐。使用最多的估计也就是面试时经常问结构体大小。最近在写自己服务器框架的二进流读...
  • weiyuefei
  • weiyuefei
  • 2016年08月05日 14:17
  • 1561

关于内存对齐的问题

在最近的项目中,我们涉及到了“内存对齐”技术。对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”。“内存对齐”应该是编译器的 “管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置...
  • u011939264
  • u011939264
  • 2016年05月31日 21:33
  • 905

知识点总结——结构体大小、内存对齐方式

1.首先是各编译器下sizeof()值: 32位编译器:32位系统下指针占用4字节 char :1个字节 char*(即指针变量): 4个字节(32位的寻址空间是2^32...
  • u010510962
  • u010510962
  • 2016年04月01日 19:27
  • 2180

C语言的内存对齐机制

from:http://blog.csdn.net/21aspnet/article/details/6729724 文章最后本人做了一幅图,一看就明白了,这个问题网上讲的不少,但是...
  • chudongfang2015
  • chudongfang2015
  • 2016年05月03日 22:35
  • 490
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:转载和积累系列 - 内存对齐
举报原因:
原因补充:

(最多只允许输入30个字)