【发布时间】:2009-03-15 17:36:09
【问题描述】:
是否有处理堆栈溢出的定义行为?
除了终止进程之外,似乎没有很多事情可以做。我只是想知道是否有人可能知道 C 标准对此有何规定。
【问题讨论】:
标签: c standards stack-overflow
是否有处理堆栈溢出的定义行为?
除了终止进程之外,似乎没有很多事情可以做。我只是想知道是否有人可能知道 C 标准对此有何规定。
【问题讨论】:
标签: c standards stack-overflow
标准不要求使用栈,也没有关于栈溢出的说法。
【讨论】:
C99 标准没有定义堆栈;它只抽象地讨论了自动或分配的存储,而带有溢出检测的连续堆栈只是实现自动存储的一种机制。
标准的第 7.14 节将 SIGSEGV 定义为发生在 “对存储的无效访问” 上的信号。 C 的实现不需要生成任何信号,但是如果检测到堆栈溢出,则使用连续的固定大小堆栈*的实现通常会发出 SIGSEGV 信号。
你可以为SIGSEGV注册一个信号处理函数,但是它不能返回-"[i]f并且当函数返回时,如果sig的值是SIGFPE,SIGILL,SIGSEGV,或者任何其他的实现- 对应于计算异常的定义值,行为[u]r 未定义"。
(* 不是我故意使用 C 实现,但我不知道 C 标准中有任何内容阻止使用用于在其他环境中实现可增长自动存储域的常用技术)
【讨论】:
C 标准甚至没有定义堆栈,所以它当然没有定义堆栈溢出时会发生什么。
【讨论】:
这里的答案是正确的,说明它与 c 标准无关,但您的“除了终止进程,似乎没有很多事情可以做”的说法不是 - 因为一般性 - 是的。
事实上,在大多数具有虚拟内存管理和按需分页的系统上,分配的堆栈非常小(通常比当前使用的多 4KB)并且经常溢出(这会产生页面错误中断)并且操作系统只是添加线程堆栈的另一页内存。
堆栈限制 - 通常 - 1MB,只是一个相当随意的数字,用于防止程序失控,通常不是绝对限制(尽管它是在一些带有英特尔处理器 IIRC 的内存模型上)。为每个线程分配 1MB 的物理内存通常是没有意义的。
【讨论】:
根据this question 的一些回答,C 标准甚至没有任何关于堆栈存在的说法,更不用说堆栈溢出了。
【讨论】:
我很确定发生的事情的细节是由操作系统定义的,但是,在所有情况下,程序都应该退出。假设一旦发生堆栈溢出(至少作为程序员),您实际上无能为力,这是正确的。您真正能做的就是从一开始就阻止它们发生。
【讨论】:
这取决于操作系统。但是,许多操作系统允许覆盖默认堆栈大小。例如,在 Windows 上,您可以使用 this linker flag 将堆栈大小从 1MB 增加到更高的值。
【讨论】:
正如其他人所提到的,标准并没有说明堆栈。
但是,我认为这是定义堆栈溢出行为,标准会说它会导致未定义的行为。
::rimshot::
【讨论】:
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,因此需要一个大的堆栈将违反“不要不要为你不需要的东西买单”。
不幸的是,程序无法说明它需要什么样的深度,也无法查询什么样的堆栈可用,唯一可以保证不参与未定义行为的程序是那些其堆栈使用量低于最低保证;在嵌入式系统世界之外,这基本上意味着该标准不保证任何比玩具更大的程序。
【讨论】: