C语言标准是否定义了堆栈溢出行为?

5

栈溢出的处理方式是否有明确的规定?

除了终止进程外,似乎没有太多可以做的。我只是想知道C标准对此有何规定。

10个回答

14

标准不要求使用堆栈,并且对于堆栈溢出没有任何说明。


8
C99标准没有定义栈;它只是抽象地讨论了自动或分配存储,而具有溢出检测的连续栈仅是实现自动存储的一种机制。
标准的第7.14节将SIGSEGV定义为发生在“对存储的无效访问”时发生的信号。C的实现不需要生成任何信号,但通常使用连续的固定大小栈*实现的实现如果检测到栈溢出,则会发出SIGSEGV信号。
您可以为SIGSEGV注册信号处理程序函数,但它不能返回-“[i] f和当函数返回时,如果sig的值为SIGFPE、SIGILL、SIGSEGV或任何其他与计算异常相应的实现定义值,则行为未定义”。
(* 我并不知道我是否曾经使用过不这样做的C实现,但我不知道C标准中是否有任何阻止在其他环境中实现可增长自动存储域所使用的常见技术的内容)

4

C标准甚至没有定义栈,因此它肯定没有定义当栈溢出时会发生什么。


4
这里的答案都正确地指出了这与c标准无关,但你的说法“除了终止进程,似乎没有什么其他事情可以做”并不是普遍适用的真实情况。实际上,在大多数具有虚拟内存管理和按需分页的系统上,分配的堆栈非常小(通常比当前使用的多4KB),经常会溢出(从而生成页面故障中断),操作系统只是为线程添加另一页内存到堆栈中。
堆栈限制通常为-1MB,只是一个相当任意的数字,用于防止程序失控,并不是绝对的限制(尽管在某些具有英特尔处理器的内存模型上确实如此)。通常不会将1MB物理内存分配给每个线程,这是没有意义的。

2

根据这个问题的一些答案,C标准甚至没有关于栈的存在性,更不用说栈溢出了。


1

我相当确定发生的具体情况是由操作系统定义的,但在所有情况下,程序应该退出。您正确地认为一旦发生堆栈溢出(至少作为程序员),您实际上无能为力。您真正能做的就是在第一时间防止它们的发生。


1

这取决于操作系统。许多操作系统允许覆盖默认的堆栈大小。例如,在Windows上,您可以使用this linker flag将堆栈大小从1MB增加到更高的值。


1

正如其他人所提到的,标准对堆栈并没有任何规定。

但是,我认为如果它要定义堆栈溢出的行为,标准会说这将导致未定义的行为。

::rimshot::


1
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(在每个调用级别上),它们的寿命都重叠,并且每个对象具有不同的地址。仅通过计数论证是不可能实现这一点的。

0

在某些系统上,确保在堆栈溢出后获得任何可预测的优势会给每个函数调用增加相当大的开销;因此,标准合理地将堆栈溢出视为未定义行为。如果目标是最大化实现可以运行合法程序的效率,这是完全适当的。

标准也不要求系统具有足够的堆栈来支持任何非平凡的函数调用深度。鉴于一些有用的程序(特别是在嵌入式系统领域)可以使用少于16字节的堆栈,并且可能不能节省更多的RAM,要求慷慨的堆栈将违反“不为你不需要的东西付费”的哲学。

不幸的是,由于程序无法说出它将需要什么样的深度,也无法查询可用的堆栈类型,因此唯一可以保证不涉及未定义行为的程序是其堆栈使用低于最小保证的程序;除了嵌入式系统领域外,这基本上意味着标准对于任何大于玩具大小的程序都不保证任何内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接