正如前面提到的,当程序中有内存访问问题时,会发生段错误。为了讨论这件事,重要的是先理解程序在内存中是如何布局的。
在Unix平台上,为程序分配的虚拟地址的布局通常类似于图4-1所示的图。
图4-1 程序内存布局 |
这里虚拟地址0在最下方,箭头显示了其中两个组件(堆和栈)的增长方向,当它们增长时,消耗掉自由区域。各个部分的作用如下所示。
文本区域,由程序源代码中的编译器产生的机器指令组成。例如,每行C代码通常会转换成两到三条机器指令,所有结果指令的集合组成了可执行文件的文本部分。这个部分的正式名称是.text。
这一组件包括静态链接代码,包括做初始化工作的系统代码/usr/lib/crt0.0,然后调用main()。
数据区域,包含在编译时分配的所有程序变量,即全局变量。
实际上,这个区域由各种各样的子区域组成。第一个子区域称为.data,由初始化过的变量组成,即在如下所示的声明中给出的变量。
还有一种用于存放未初始化数据的.bss区域,在如下所示的声明中给出。
当程序在运行时从操作系统中请求额外的内存时(例如,当在C语言中调用malloc()时,或者在C++中调用new结构时),请求的内存在名为堆的区域中分配。如果堆空间不够,可以通过调用brk()来扩展堆(这正是malloc()及相关函数所做的事情)。
栈区域,是用来动态分配数据的空间。函数调用的数据(包括参数、局部变量和返回地址)都存储在栈上。每次进行函数调用时栈都会增长,每次函数返回到其调用者时栈都会收缩。
由于位置的平台依赖性,图4-1中没有显示程序的动态链接代码,但是动态链接代码确实在某个地方存在。