认识计算机内存
指针是C语言中重要的数据类型,它赋予了C程序直接访问和修改内存中数据的能力。在学习指针之前,我们先来了解一下计算机中的内存。
早期计算机的内存只有千字节大小,随着计算机性能提升,计算机对内存大小的要求也随之提高,到目前为止,计算机内存已达到128GB,甚至更大。内存具有线性存储的特点,并且内存具有真实的物理地址映射,内存以1个字节作为存储单元,每存储单元具有唯一的地址编号。这意味着可以通过寻址的方式获取内存中的数据,即通过内存地址获取内存中的数据或修改内存中的数据。
计算机的虚拟内存地址与物理地址的映射如图1所示。
图1 虚拟地址和物理内存地址映射
在图1中,是4G内存空间的地址编号,左边是用十六进制表示的内存地址编号,右边是内存空间。内存空间中的每一个小方格代表一个bit位,每一行有8个方格(8bit),即一行代表的内存大小为1个字节,每一个字节都有一个地址编号。
真实的物理内存空间由操作系统虚拟内存映射进行管理,具有严格的地址区间划分。当运行程序时,操作系统会把程序装载到内存,并为其分配相应的内存空间,存储程序中的变量、常量、函数代码等。程序在内存中的分布区域如图2所示。
图2 进程的内存空间分布
由图2可知,当有程序运行时,操作系统将内存划分成7个部分,每一部分内存空间都有严格的地址范围。其中,灰色区域为程序可用部分。在内存中,每一段内存空间都有严格的内存地址范围。下面结合图2分别介绍内存空间各部分的名称及含义。
(1)系统内核空间:系统内核空间供操作系统使用,不允许用户直接访问。
(2)栈空间:用于存储局部变量等数据,在为局部变量分配空间时,栈总是从高地址空间向低地址空间分配内存,即栈内存的增长方式是从高内存地址到低内存地址。
(3)动态库内存空间:用于加载程序运行时链接的库文件。
(4)堆空间:由用户自己申请释放,其增长方式是从低到高。
(5)读/写数据内存空间:用于存储全局变量、静态全局变量等数据。
(6)只读代码/数据内存空间:用于存储函数代码、常量等数据。
(7)保留区间:保留区间是内存的起始地址,具有特殊的用途,不允许用户访问。
C语言程序编译运行过程中,主要涉及到的内存空间包括栈、堆、数据段、代码段,这四部分就是C程序员通常所说的内存四区,下面分别进行介绍。
(1)栈
栈(Stack)是用来存储函数调用时的临时信息区域,一般只有10兆左右大小的空间,栈顶地址和栈大小是系统预先规定好的。栈内存主要用于数据交换,如函数传递的参数、函数返回地址、函数的局部变量等。栈内存由编译器自动分配和释放。
(2)堆
堆(Heap)是不连续的内存区域,各部分区域由链表将它们串联起来。堆内存是由内存申请函数获取,由内存释放函数归还。若申请的内存空间在使用完成后不释放,会造成内存泄漏。堆内存的大小是由系统中有效的虚拟内存决定的,可获取的空间较大,而且获得空间的方式也比较灵活。
虽然堆区的空间较大,但必须由程序员自己申请,而且在申请的时候需要指明空间的大小,使用完成后需要手动释放。另外,由于堆区的内存空间是不连续的,容易产生内存碎片。
(3)数据段
数据段(Data)可分为三个部分,bss段、data段、常量区。其中,bss段用于存储未初始化或初始化为0的全局变量、静态变量;data段存储初始化的全局变量、静态变量;常量区存储字符串常量和其他常量存储。
(4)代码段
代码段也称为文本段(Text Segment),存放的是程序编译完成后的二进制机器码和处理器的机器指令,当各个源文件单独编译之后生成目标文件,经链接器链接各个目标文件并解决各个源文件之间函数的引用,可执行的程序指令通过从代码段获取得以运行。