栈为什么从高地址往低地址分配内存
原因
栈的栈顶在低地址,栈底在高地址这样设计有什么意义呢?
计算机内存分了代码段(.text段)、初始化的数据段(.data段)、未初始化的数据段(.bss段)、堆空间(heap)、栈空间(stack)和命令行参数和环境变量区域。
程序计数器(Program Counter,简称PC)的缺省指向0地址,计算机开机后从程序计数器指向的地址开始执行程序,每执行完一条指令后, 程序计数器自动加1。
因此很自然的,代码段从低地址区间开始加载,向高地址区间扩展;
heap从低地址向高地址扩展,做内存管理相对要简单些,为了避免栈空间和代码段冲突,最大利用地址空间,很自然的,我们会选择把栈底设置在高地址区间,然后让栈向下增长。
这是来自apue里一张经典的c程序内存分布图,着重看一下heap和stack的内存分布。
栈由高地址向低地址扩展的优点
stack从高地址向低地址扩展,这样栈空间的起始位置就能确定下来。动态的调整栈空间大小也不需要移动栈内的数据,如果是从低地址到高地址的扩展,结尾的地址是固定的,如果要扩大或缩小,则需要移动整个栈的数据。
并且这样设计可以使得堆和栈能够充分利用空闲的地址空间。如果栈向上涨的话,我们就必须得指定栈和堆的一个严格分界线,但这个分界线怎么确定呢?平均分?但是有的程序使用的堆空间比较多,而有的程序使用的栈空间比较多。
所以就可能出现这种情况:一个程序因为栈溢出而崩溃的时候,其实它还有大量闲置的堆空间呢,但是我们却无法使用这些闲置的堆空间。所以呢,最好的办法就是让堆和栈一个向上涨,一个向下涨,这样它们就可以最大程度地共用这块剩余的地址空间,达到利用率的最大化
现在 CPU 指令集的设计
大部分CPU指令集设计了函数调用架构,定义了专用的调用/返回指令,并在指令中隐含规定栈的方向。
- 主流1:向低地址扩展:x86,MIPS
- 主流2:自由选择:Arm(但个别指令仅支持向低)
- 罕见:向高地址扩展:PA-RISC,操作系统Multics
- 非主流:System z,栈是个链表[2]
其他解释
1.栈内内存是连续分配
因位栈空间内存分配连续,如果给一个数组或对象分配内存,栈会优先选择还没有分配的最小的内存地址给数组,数组中的地址是从低地址到高地址依次分配。所以数组的第一个元素的起始地址就是给数组分配的最低地址
2.栈的栈顶指针ESP默认指向栈顶
对数组的访问一般都是对一个数组的起始地址进行操作,也就是说我们需要的是数组的起始地址->也就是低地址,由于栈顶指针默认指向的是栈顶元素,那么只能是栈顶指针指向低地址值-->这样便于对数组的访问。如果栈还是采用从低地址到高地址的扩展,那么就不会默认指向数组的起始地址(数组指针),不便于访问。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 风起之时'blog!













