可能是重复问题:
什么是堆和栈?它们在哪里?
关于 c 程序中的内存布局基本概念,我了解到:
- 该语言使用两个主要的数据结构栈(stack)和堆(heap)。
- 栈被创建用于存储子例程的本地变量和书记数据。
- 堆被创建用于存储程序动态分配的变量。
- 堆的长度是可变的。(对于栈不太确定)
- 通常,在执行之前,编译器/语言请求操作系统创建这些数据结构的责任。
问题
- 栈/堆的初始大小是多少?由谁决定?
- 它们在物理内存中的位置是什么?我看到一个概括性描述:"栈被创建在最高地址,堆在最低地址"请详细说明。
可能是重复问题:
什么是堆和栈?它们在哪里?
关于 c 程序中的内存布局基本概念,我了解到:
#include <stdlib.h>
#include <stdio.h>
void check(int depth) {
char c;
char *ptr = malloc(1);
printf("stack at %p, heap at %p\n", &c, ptr);
if (depth <= 0) return;
check(depth-1);
}
int main() {
check(10);
return 0;
}
在我的机器上,我看到:
stack at 0x22ac3b, heap at 0x20010240
stack at 0x22ac0b, heap at 0x200485b0
stack at 0x22abdb, heap at 0x200485c0
stack at 0x22abab, heap at 0x200485d0
stack at 0x22ab7b, heap at 0x200485e0
stack at 0x22ab4b, heap at 0x200485f0
stack at 0x22ab1b, heap at 0x20048600
stack at 0x22aaeb, heap at 0x20048610
stack at 0x22aabb, heap at 0x20048620
stack at 0x22aa8b, heap at 0x20048630
stack at 0x22aa5b, heap at 0x20048640
因此,堆栈向下增长,堆向上增长(正如您可能根据传说所预期的那样),但是堆栈具有较小的地址,并且它们不会相互靠近增长(这个传说已经被打破了)。
顺便说一句,我的check
函数是尾递归的,在某些实现中,使用某些编译器选项,您可能看不到堆栈移动。这告诉您关于为什么标准没有强制规定所有这些工作方式的原因--如果它这样做,可能会无意中禁止有用的优化。
如前所述,大小是特定于操作系统的。例如,在使用Visual Studio的Windows上,默认堆栈大小为1MB。
在Linux上,以下命令可以显示您当前的堆栈大小。
ulimit -s or -a
在我的Linux Mint 64位系统上,它显示为8192 KB。
每个程序在加载到内存时都有几个段。在汇编语言中,可以使用.data,.code等前缀(intelx86)来指示它们中的每一个。
数据段有多个子段,堆栈和堆都是其一部分,还有其他几个子段。
堆栈也可以隐式增长,即当您进行另一个函数调用时,会将激活记录推入堆栈,从而利用堆栈的更多内存。这就是为什么无限递归导致程序用完分配的堆栈时崩溃的原因。
当函数调用返回时,该激活记录被弹出,堆栈会缩小。
相反,堆从相反方向增长,并包含所有动态分配的内存。
这两个段增长方向相反的原因是为了最大化它们的组合内存利用率。请注意,正如评论中提到的,这不是C标准,但大多数常见操作系统都已实现此功能。
------堆栈开始 ----------- 堆栈向下增长
--------除非它们彼此交叉,否则程序可以正常运行。
-------堆开始 ------------堆向上增长
如果程序不使用堆,则堆栈可以利用最大的内存,包括堆的内存。如果程序进行少量递归调用并使用最少的本地变量(即使用较少的堆栈内存),则它可以最大程度地利用堆。
数据段的其他部分是BSS等,可能包含未初始化的静态变量字段。堆栈/堆的初始大小是多少?由谁决定?
这取决于编译器和操作系统。
它们在物理内存的哪里创建?一般描述是"堆创建在顶端地址,栈创建在低端地址"。
这也取决于编译器和操作系统。
实际上,语言标准没有规定最小堆栈大小,也没有明确指定堆栈或堆在内存中的位置。原因是为了使C程序不那么依赖于这些细节,从而使其在不同平台(即不同操作系统、不同CPU和不同编译器)上更具可移植性。
stack at 0x7fff356d5fd7, heap at 0x1d39010
。运行sudo bash -c 'for x in /proc/*/maps; do echo $x; egrep stack\|heap $x; done'
,可以看到所有进程都类似的输出。2014年曾经利用堆栈和堆重叠的方式进行攻击:“所有这些命令行参数的影响是膨胀堆栈(向下增长)和堆(向上增长),直到它们相互碰撞。” - bain