是否存在处理堆栈溢出的已定义行为?
除了终止这个过程外,似乎并没有完成很多工作。我只是想知道是否有人可能知道C标准对此有何看法。
答案 0 :(得分:14)
该标准不需要使用堆栈,并且没有任何关于堆栈溢出的说法。
答案 1 :(得分:8)
C99标准没有定义堆栈;它只讨论摘要中的自动或分配存储,而具有溢出检测的连续堆栈只是实现自动存储的一种机制。
该标准的第7.14节将SIGSEGV定义为“对存储的无效访问”上发生的信号。 C的实现不需要生成任何信号,但是如果检测到堆栈溢出,则使用连续的固定大小堆栈*的实现通常用信号SIGSEGV。
您可以为SIGSEGV注册信号处理函数,但它不能返回 - “[i] f,当函数返回时,如果sig的值是SIGFPE,SIGILL,SIGSEGV或任何其他实现 - 对应于计算异常的定义值,行为[u] r未定义“。
(*不是我故意使用C实现,但我不知道C标准中的任何内容阻止使用在其他环境中实现可增长的自动存储域的常用技术) / p>
答案 2 :(得分:4)
C标准甚至没有定义堆栈,所以它当然没有定义堆栈溢出时会发生什么。
答案 3 :(得分:4)
这里的答案是正确的,说它与c标准无关,但是你的陈述“除了终止这个过程,似乎没有一件事可以完成”不是 - 如一般性 - 是的。
事实上,在大多数具有虚拟内存管理和按需分页的系统上,分配的堆栈非常小(通常比当前使用的堆栈多4KB)并且经常溢出(产生页面错误中断)并且操作系统只是添加另一页内存到线程的堆栈。
堆栈限制 - 通常 - 1MB,只是一个相当随意的数字,用于防范失控程序并且通常不是绝对限制(尽管它是在某些具有intel处理器IIRC的内存模型上)。通常,为每个线程分配1MB的物理内存是没有意义的。
答案 4 :(得分:2)
根据this question的一些答案,C标准甚至没有任何关于堆栈存在的说法,更不用说堆栈溢出了。
答案 5 :(得分:1)
我很确定操作系统定义了发生的具体细节,但是在所有情况下,程序都应该退出。你是正确的,假设一旦发生堆栈溢出,你真的无能为力(至少作为程序员)。你所能做的就是首先防止它们发生。
答案 6 :(得分:1)
由操作系统决定。但是,许多操作系统允许覆盖默认堆栈大小。例如,在Windows上,您可以使用this linker flag将堆栈大小从1 MB增加到更高的值。
答案 7 :(得分:1)
正如其他人所提到的那样,该标准没有说明任何关于堆栈的内容。
但是,我认为它是定义堆栈溢出行为,标准会说它会导致未定义的行为。
::边敲击::
答案 8 :(得分:0)
在某些系统上,在堆栈溢出后确保任何可预测的优势会给每个函数调用增加相当大的开销;因此,该标准非常合理地将堆栈溢出视为未定义行为。如果目标是最大化实现可以运行合法程序的效率,那么这是完全合适的。
该标准也不要求系统有足够的堆栈来支持任何非平凡的函数调用深度。鉴于一些有用的程序(特别是在嵌入式系统世界中)可以通过少于16个字节的堆栈来获得,并且可能不一定能够节省比这更多的RAM,需要大量堆栈会违反&#的哲学34;不要为你不需要的东西买单"。
不幸的是,事实上程序没有办法说出它需要什么样的深度,也没有查询可用的堆栈类型,唯一可以保证不参与的程序未定义的行为是那些堆栈使用率低于最低保证的行为;在嵌入式系统世界之外,这基本上意味着标准不保证任何比玩具更大的程序。
答案 9 :(得分:0)
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
(在每个调用级别),其生命周期全部重叠,每个都有不同的地址。仅通过计数论证就不可能做到这一点。