分配了多少内存给调用堆栈?

Sou*_*a B 0 c++ callstack memory-management segmentation-fault

以前我见过很多C++函数的汇编。在 gcc 中,所有指令都以以下指令开头:

push    rbp
mov     rbp, rsp
sub     rsp, <X>  ; <X> is size of frame
Run Code Online (Sandbox Code Playgroud)

我知道这些指令存储前一个函数的帧指针,然后为当前函数设置一个帧。但在这里,程序集既不要求映射内存(如 malloc),也不检查所指向的内存是否rbp分配给进程。

因此它假设启动代码已经为调用堆栈的整个深度映射了足够的内存。那么到底为调用堆栈分配了多少内存呢?启动代码如何知道调用栈的最大深度?

这也意味着,我可以远距离访问越界数组,因为虽然它不在当前帧中,但它映射到进程。所以我写了这段代码:

int main() {
    int arr[3] = {};
    printf("%d", arr[900]);
}
Run Code Online (Sandbox Code Playgroud)

当索引为 900 时,它会退出。SIGSEGV但令人惊讶的是,当索引为 901 时,它不会退出。同样,SIGSEGV对于某些随机索引,它会退出,而对于某些索引则不会。在编译器资源管理器中使用 gcc-x86-64-11.2 进行编译时观察到此行为。

eer*_*ika 7

启动代码如何知道调用栈的最大深度?

事实并非如此。

在大多数常见的实现中,堆栈的大小是恒定的。

如果程序超出了常量大小的堆栈,则称为堆栈溢出。这就是为什么您必须避免在自动存储中创建大型对象(通常是但不一定是数组),以及为什么必须避免具有线性深度的递归(例如递归链表算法)。

那么到底为调用堆栈分配了多少内存呢?

在大多数桌面/服务器系统上,它是可配置的,并且默认为一到几兆字节。在嵌入式系统上它可能要少得多。

当索引为 900 时,会以 SIGSEGV 退出。但令人惊讶的是,当索引为 901 时则不会。

在这两种情况下,程序的行为都是未定义的。

是否可以知道分配的堆栈大小?

是的。您可以阅读目标系统的文档。如果您打算编写可移植程序,那么您必须假设所有目标系统的最小值。对于桌面/服务器,我提到的 1 MB 是合理的。

C++ 中没有获取大小的标准方法。