是否有处理堆栈溢出的定义行为?
除了终止进程之外,似乎没有很多事情可以做。我只是想知道是否有人可能知道 C 标准对此有何评论。
该标准不要求使用堆栈,也没有提及堆栈溢出。
C99 标准没有定义堆栈。它只抽象地讨论了自动或分配的存储,而带有溢出检测的连续堆栈只是实现自动存储的一种机制。
该标准的第 7.14 节将 SIGSEGV 定义为在“对存储的无效访问”时出现的信号。C 的实现不需要生成任何信号,但如果检测到堆栈溢出,则使用连续固定大小堆栈*的实现通常会发出 SIGSEGV 信号。
您可以为 SIGSEGV 注册一个信号处理函数,但它不能返回 - "[i]f 并且当函数返回时,如果 sig 的值是 SIGFPE、SIGILL、SIGSEGV 或任何其他与 a 对应的实现定义的值计算异常,behavio[u]r 未定义”。
(* 并不是我故意使用 C 实现,但我不知道 C 标准中的任何内容阻止使用用于在其他环境中实现可增长自动存储域的常用技术)
C 标准甚至没有定义堆栈,因此它当然没有定义堆栈溢出时会发生什么。
这里的答案是正确的,说明它与 c 标准无关,但你的说法“除了终止进程,似乎没有很多事情可以做”——一般来说——真的。
事实上,在大多数具有虚拟内存管理和按需分页的系统上,分配的堆栈非常小(通常比当前使用的多 4KB)并且经常溢出(这会产生页面错误中断)并且操作系统只是添加另一个页面内存到线程的堆栈。
堆栈限制(通常为 1MB)只是为防止程序失控而选择的一个相当随意的数字,通常不是绝对限制(尽管它是在某些带有英特尔处理器 IIRC 的内存模型上)。为每个线程分配 1MB 的物理内存通常是没有意义的。
根据this question的一些答案,C标准甚至没有关于堆栈的存在说什么,更不用说堆栈溢出了。
我相当确定发生的事情的细节是由操作系统定义的,但是,在所有情况下,程序都应该退出。您正确地假设一旦发生堆栈溢出(至少作为程序员),您实际上无能为力。你真正能做的就是从一开始就阻止它们发生。
这取决于操作系统。但是,许多操作系统允许覆盖默认堆栈大小。例如,在 Windows 上,您可以使用此链接器标志将堆栈大小从 1MB 增加到更高的值。
正如其他人所提到的,该标准没有说明堆栈。
但是,我认为这是定义堆栈溢出行为,标准会说它会导致未定义的行为。
::边缘射击::
C标准在这方面是矛盾的。考虑以下程序:
void foo(uintptr_t n)
{
int a;
printf("%p\n", (void *)&a);
if (n+1) foo(n+1);
}
int main()
{
int a;
printf("%p\n", (void *)&a);
foo(0);
}
该程序完全符合要求,不违反任何最低翻译限制,正如其他人所说,标准语言中没有关于堆栈限制/溢出的任何内容。但是,它会产生UINTPTR_MAX
+2 个对象a
(在每个调用级别),它们的生命周期都重叠,每个对象都有不同的地址。这是不可能的,仅靠计数论证。
在某些系统上,确保堆栈溢出后任何一种可预测的好处都会给每个函数调用增加相当大的开销;因此,该标准相当合理地将堆栈溢出视为未定义行为。如果目标是最大限度地提高实现运行合法程序的效率,这是完全合适的。
该标准也不要求系统有足够的堆栈来支持任何重要的函数调用深度。鉴于一些有用的程序(尤其是在嵌入式系统领域)可以使用少于 16 字节的堆栈,并且可能不一定能够腾出更多的 RAM,因此需要一个大的堆栈将违反“不要'不要为你不需要的东西买单'。
不幸的是,程序无法说明它需要什么样的深度,也无法查询什么样的堆栈可用,唯一可以保证不参与未定义行为的程序是那些堆栈使用低于最低保证;在嵌入式系统世界之外,这基本上意味着该标准不保证任何比玩具大的程序。