仍是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,char占一个byte,那么将它们放到一个结构体中应该占4+1=5byte;可是实际上,经过运行程序获得的结果是8 byte,这就是内存对齐所致使的。linux
//32位系统 #include<stdio.h> struct{ int x; char y; }s; int main() { printf("%d\n",sizeof(s); // 输出8 return 0; }
现代计算机中内存空间都是按照 byte 划分的,从理论上讲彷佛对任何类型的变量的访问能够从任何地址开始,可是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(一般它为4或8)的倍数,这就是所谓的内存对齐。算法
尽管内存是以字节为单位,可是大部分处理器并非按字节块来存取内存的.它通常会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,咱们将上述这些存取单位称为内存存取粒度.编程
如今考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。小程序
假如没有内存对齐机制,数据能够任意存放,如今一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),而后从地址4开始读取下一个4字节块,一样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这须要作不少工做.缓存
如今有了内存对齐的,int类型数据只能存放在按照对齐规则的内存中,好比说0地址开始的内存。那么如今该处理器在取数据时一次性就能将数据读出来了,并且不须要作额外的操做,提升了效率。服务器
每一个特定平台上的编译器都有本身的默认“对齐系数”(也叫对齐模数)。gcc中默认#pragma pack(4),能够经过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这一系数。
有效对其值:是给定值#pragma pack(n)和结构体中最长数据类型长度中较小的那个。有效对齐值也叫对齐单位。数据结构
了解了上面的概念后,咱们如今能够来看看内存对齐须要遵循的规则:
(1) 结构体第一个成员的偏移量(offset)为0,之后每一个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,若有须要编译器会在成员之间加上填充字节。并发
(3) 结构体的总大小为 有效对齐值 的整数倍,若有须要编译器会在最末一个成员以后加上填充字节。
下面给出几个例子以便于理解:ide
//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 默认值不一样。而咱们能够经过预编译命令#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/
【福利】本身搜集的网上精品课程视频分享(上)
【系统设计】LRU缓存
【数据结构与算法】 通俗易懂讲解 二叉搜索树
【数据结构与算法】 通俗易懂讲解 链表
【数据结构与算法】 通俗易懂讲解 位排序
【C++札记】C++11并发编程(一)开启线程之旅
【C++札记】C/C++指针使用常见的坑
码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!