本文件主要整理自:
Data alignment: Straighten up and fly right
更多关于C++内存布局请参考:
源文件经过以下几步生成可执行文件:
编译器和汇编器创建的目标文件包含:二进制代码(指令)、源码中的数据;链接器将多个目标文件链接成一个;装载器吧目标文件加载到内存。
图1 源文件到可执行文件的步骤
通过上面的小节,我们知道将源程序转换为可执行程序的步骤,典型的可执行文件分为两部分:
源程序编译后链接到一个以0地址为始地址的线性或多维虚拟地址空间。而且每个进程都拥有这样一个空间,每个指令和数据都在这个虚拟地址空间拥有确定的地址,把这个地址称为虚拟地址(Virtual Address)。将进程中的目标代码、数据等的虚拟地址组成的虚拟空间称为虚拟存储器(Virtual Memory)。典型的虚拟存储器中有类似的布局:
如下图所示:
图2 进程内存布局
当进程被创建时,内核为其提供一块物理内存,将虚拟内存映射到物理内存,这些都是由操作系统来做的。
讨论C/C++中的内存布局,不得不提的是数据的存储类别!数据在内存中的位置取决于它的存储类别。一个对象是内存的一个位置,解析这个对象依赖于两个属性:存储类别、数据类型。
C/C++中由(auto、 extern、 register、 static)存储类别和对象声明的上下文决定它的存储类别。
auto和register将声明的对象指定为自动存储类别。他们的作用域是局部的,诸如一个函数内,一个代码块{***}内等。操作了作用域,对象会被销毁。
静态对象可以局部的,也可以是全局的。静态对象一直保持它的值,例如进入一个函数,函数中的静态对象仍保持上次调用时的值。包含静态对象的函数不是线程安全的、不可重入的,正是因为它具有“记忆”功能。
下面我们分析一段代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int a; 5 static int b; 6 7 void func( void ) 8 { 9 char c; 10 static int d; 11 } 12 13 int main( void ) 14 { 15 int e; 16 int *pi = ( int *) malloc ( sizeof ( int )); 17 func (); 18 func (); 19 free (pi ); 20 return (0); 21 }
程序中声明的变量a、b、c、d、e、pi的存储类别和生命期如下所述:
用图表示如下:
图3 例子的内存布局
综合1~4,介绍了C/C++中由源程序到可执行文件的步骤,和可执行程序的内存布局,数据存储类别,最后还通过一个例子来说明。
可执行程序中的变量在内存中的布局可以总结为如下:
内存可以分为以下几段:
前1~4引自吴秦先生的博文。
作者:吴秦
出处:http://www.cnblogs.com/skynet/
本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接).
先来看一个例子:
1 #include<iostream> 2 using namespace std; 3 4 class test 5 { 6 private: 7 char c = ‘1‘; // 1byte 8 int i; // 4byte 9 short s = 2; // 2byte 10 }; 11 12 int main() 13 { 14 cout << sizeof(test) << endl; 15 return 0; 16 }
输出是12.
1 #include<iostream> 2 using namespace std; 3 4 class test 5 { 6 private: 7 int i; // 4byte 8 char c = ‘1‘; // 1byte 9 short s = 2; // 2byte 10 }; 11 12 int main() 13 { 14 cout << sizeof(test) << endl; 15 return 0; 16 }
输出是8.
我们可以看到,类test和test2的成员变量完全一样,只是定义顺序不一样,却造成了2个类占用内存大小不一样。这就是编译器内存对齐的缘故。
1、第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、在数据成员完成各自对齐之后,类(结构或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
很明显#pragma pack(n)作为一个预编译指令用来设置多少个字节对齐的。值得注意的是,n的缺省数值是按照编译器自身设置,默认为8。其语法如下:
where:
1 | 2 | 4 | 8 | 16 Members of structures are aligned on the specified byte-alignment, or on their natural alignment boundary, whichever is less, and the specified value is pushed on the stack. nopack No packing is applied, and "nopack" is pushed onto the pack stack pop The top element on the pragma pack stack is popped. (no argument specified) Specifying #pragma pack() has the same effect as specifying #pragma pack(pop).
内存分配过程:
1)char和编译器默认的内存缺省分割大小比较,char比较小,分配一个字节给它。
2)int和编译器默认的内存缺省分割大小比较,int比较小,占4字节。只能空3个字节,重新分配4个字节。
3)short和编译器默认的内存缺省分割大小比较,short比较小,占2个字节,分配2个字节给它。
4)对齐结束类本身也要对齐,所以最后空余的2个字节也被test占用。
1)int和编译器默认的内存缺省分割大小比较,int比较小,占4字节。分配4个字节给int。
2)char和编译器默认的内存缺省分割大小比较,char比较小,分配一个字节给它。
3)short和编译器默认的内存缺省分割大小比较,short比较小,此时前面的char分配完毕还余下3个字节,足够short的2个字节存储,所以short紧挨着。分配2个字节给short。
4)对齐结束类本身也要对齐,所以最后空余的1个字节也被test占用。
1 #include<iostream> 2 using namespace std; 3 4 #pragma pack(1)//设定为1字节对齐 5 6 class test 7 { 8 private: 9 char c = ‘1‘; //1byte 10 int i; //4byte 11 short s = 2; //2byte 12 }; 13 14 class test2 15 { 16 private: 17 int i; //4byte 18 char c = ‘1‘; //1byte 19 short s = 2; //2byte 20 }; 21 22 int main() 23 { 24 cout << sizeof(test) << endl; 25 cout << sizeof(test2) << endl; 26 return 0; 27 }
输出结果:
可以看到,当我们把编译器的内存分割大小设置为1后,类中所有的成员变量都紧密的连续分布。
要严重参考一IBM的文章:Data alignment: Straighten up and fly right
l 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
l 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。
这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。
CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 可以把它翻译为“内存读取粒度” 。
假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:
1)数据从0字节开始
2)数据从1字节开始
假设内存读取粒度为4。
当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。
当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。
此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。
这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。
原文:http://www.cnblogs.com/xiehongfeng100/p/4372342.html