C/C++把内存划分为许多区域,主要的内存区域有:
全局内存空间,又称为“(全局)静态存储区static memory allocation”。编程人员不需要为全局变量和静态变量明确划分存储空间凡是由源程序声明的全局变量、全局数组,编译器都能够在数据段或常量段为其分配适当的存储空间。由于整个程序都可以访问这个区域的数据,所以人们认为使用这种存储空间数据会破坏程序的结构化体系。此外,在全局内存区存储数据之前,必须事先声明其确切的容量。因而这个空间不适用于存储缓存或动态数组。在全局内存空间出现的缓冲区溢出问题,往往将覆盖在内存中相邻位置的变量或缓存(请参阅本书7.2节的案例)。
栈空间,即分配给栈的存储区域。它是由编译器自动分配、释放的存储区域,常用于存放函数的参数和局部变量。在特定情况下,局部变量可被其他函数访问(局部变量的指针作为参数传递给被调用方函数)。在指令层面,“分配和释放栈空间的实质就是调整SP寄存器的值,因而分配和释放栈空间的操作速度非常快。编译器只能为那些在编译阶段确定存储空间的局部变量分配栈空间。因此,无法事先预判存储空间的缓冲型数据类型,即缓冲区和动态数组[1]不会被分配到栈空间里。在栈空间发生的缓冲区溢出问题,通常会篡改栈里的重要数据(请参阅本书18.2节的案例)案例)。
堆空间,即“动态内存分配区。C语言的malloc()/free()函数或C++的new/delete语句即可分配、释放堆空间)。“不必事先声明堆空间的大小”即“可在程序启动以后再确定数据块的容量”这一特征构成了其独特的便利性。另外,程序员还可以动态调整(realloc()函数)内存块的大小,只是调整堆空间的操作速度不很理想。在内存分配操作中,申请、释放堆空间的操作是速度最慢的操作:在分配、释放堆空间时,进行这种操作的程序必须支持并且更新所有控制结构。在这个区域发生的缓冲区溢出经常会覆盖堆空间的数据结构体。堆空间管理不当还会发生内存泄露问题:所有被分配的堆空间都应当被明确地释放,否则就会出现内存泄露问题。但是程序员可能会出现“忘记释放堆空间”的问题,还有发生释放不彻底的问题。另外,“在调用free()函数释放空间之后再次直接使用这块内存区域”的指令同样会带来非常严重的安全问题(请参阅本书21.2节的案例)。
[1] 除非像5.2.4节那样使用alloca()函数。