全面的解释可能超出了本论坛的范围。整篇文章都致力于这个主题。但是,从简单的角度来看,您可以这样看待它。
编译器不会在内存中布置代码。它确实假设它自己拥有整个内存区域。编译器生成目标文件,其中目标文件中的符号通常从偏移量 0 开始。
链接器负责将对象文件拉到一起,将符号链接到链接对象中的新偏移位置,并生成可执行文件格式。
链接器也不在内存中布置代码。它将代码和数据打包到通常标记.text
为可执行代码指令以及.data
全局变量和字符串常量之类的部分中。(还有其他部分用于不同的目的)链接器可能会向操作系统加载器提供重定位符号的提示,但加载器不必强制执行。
操作系统加载程序解析可执行文件并决定代码和数据在内存中的布局位置。其位置完全取决于操作系统。通常,堆栈位于比程序指令和数据更高的内存区域并向下增长。
每个程序的编译/链接都假设它拥有自己的整个地址空间。这就是虚拟内存的用武之地。它对程序完全透明,完全由操作系统管理。
虚拟内存的范围通常从地址 0 到平台支持的最大地址(不是无穷大)。这个虚拟地址空间被操作系统划分为内核可寻址空间和用户可寻址空间。假设在一个假设的 32 位操作系统上,上面的地址0x80000000
是为操作系统保留的,下面的地址是供程序使用的。如果程序试图访问此分区之上的内存,它将被中止。
操作系统可以决定堆栈从最高可寻址用户内存开始,并随着位于低得多的地址的程序代码向下增长。
堆的位置通常由您构建程序的运行时库管理。它可以从您的程序代码和数据之后的下一个可用地址开始。