C程序在内存中的布局与操作系统(甚至于硬件)密切相关。在32位系统中,内存空间具有4GB(2的32次方)的寻址能力,应用程序可以直接使用32位的地址进行寻址,整个内存空间是一个统一的地址空间。这样的内存方式称为平坦(flat)的内存模型。
4GB的用户空间中的一部分是给操作系统内核使用的,应用程序是无法访问这段内存的,这一部分内存地址称为内核空间。Window在默认情况下会将高地址的2GB空间分配给内核(也可配置为1GB),而Linux默认情况下将高地址的1GB空间分配给内核。用户使用剩下的2GB或3GB的内存空间称为用户空间。
在用户空间中,应用程序使用的内存空间一般还要分为不同的区域。
图1 典型的Linux地址空间分配
C程序经过编译之后将内存分为几个不同的区域,主要包括:程序代码区、栈区、堆区和全局(静态)存储区等。各个区域主要功能如下:
(1)程序代码区(只读代码段):存放程序的可执行文件(二进制代码)在内存中的映像。由装载器在装载时将可执行文件的内存映像装入。
(2)栈(stack):用于维护函数调用的上下文,由编译器进行管理,自动分配和释放。存放函数调用过程中的各种参数、局部变量、返回值以及函数返回地址。操作方式类似数据结构中的栈。
(3)堆(heap):用于程序动态申请分配和释放空间。C语言中的malloc和free,C++中的new和delete均是在堆中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则应用程序结束时操作系统自动回收。注意:这里的“堆”并不是数据结构中的“堆”。
(4)全局(静态)存储区(读写数据段):分为data段和bss段。data段(全局初始化区)存放初始化的全局变量和静态变量;bss段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中bss段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。
此外,还有文字常量区:存放常量字符串。程序结束后由系统释放。动态链接库映射区:用于映射装载的动态链接库。保留区并不是一个单一的内存区域,而是对内存中受到保护而禁止访问的内存区域的总称。在大多数操作系统中,极小的地址通常都不允许访问。
【知识拓展】有兴趣进行深入研究的读者可以阅读电子工业出版社2009年出版的俞甲子、石凡和潘爱民所著的《程序员的自我修养——链接、装载与库》一书。
《横扫offer---程序员招聘真题详解700题》,开点工作室著,清华大学出版社出版,天猫、京东等各大网上书店及实体书店均有发售。