nne*_*neo 25 c c++ undefined-behavior language-lawyer
这是一个C规范问题.
我们都知道这是合法的C,应该可以在任何平台上正常工作:
/* Stupid way to count the length of a number */
int count_len(int val) {
char buf[256];
return sprintf(buf, "%d", val);
}
Run Code Online (Sandbox Code Playgroud)
但这几乎可以保证崩溃:
/* Stupid way to count the length of a number */
int count_len(int val) {
char buf[256000000];
return sprintf(buf, "%d", val);
}
Run Code Online (Sandbox Code Playgroud)
不同之处在于后一个程序会破坏堆栈并且可能会崩溃.但是,纯粹在语义上,它与以前的程序没有任何不同.
根据C规范,后一个程序实际上是未定义的行为吗?如果是这样,它与前者的区别是什么?如果不是,那么C规范中的说法是否可以使符合要求的实现崩溃?
(如果这在C89/C99/C11/C++*之间有所不同,那么这也很有趣).
语言标准C(89,99,11)与此措词(在一些C还发现++,C#,Fortran和帕斯卡标准)一个范围段开始:
本国际标准未规定
gcc编译器确实提供了在运行时检查堆栈溢出的选项
21.1堆栈溢出检查
对于大多数操作系统,默认情况下,gcc不会执行堆栈溢出检查。这意味着,如果主环境任务或某些其他任务超出了可用的堆栈空间,则将发生不可预测的行为。大多数本机系统通过在每个任务堆栈的末尾添加保护页面来提供某种程度的保护。这种机制通常不足以正确处理堆栈溢出情况,因为较大的局部变量可能会“跳到”保护页面上方。此外,当命中保护页时,堆栈上可能没有剩余空间可用于执行异常传播代码。启用堆栈检查可以避免这种情况。要激活堆栈检查,请使用gcc选项-fstack-check编译所有单元。例如:
gcc -c -fstack-check package1.adb
Run Code Online (Sandbox Code Playgroud)
使用此选项编译的单元将生成额外的指令,以检查对堆栈的任何使用(用于过程调用或在声明块中声明局部变量)是否不超过可用的堆栈空间。如果超出空间,则会引发Storage_Error异常。
在标准化过程中,C99曾试图在标准中做出更强有力的声明,即规模和复杂性超出了标准范围,但实施者有责任记录这些限制。
理由是
一致性的定义一直是C标准的一个问题,被一位作者描述为“甚至没有橡胶齿,更像是橡胶胶”。尽管与C89相比C9X有所改进,但许多问题仍然存在。
本文提出了一些变化,这些变化虽然不完美,但有望改善这种情况。
建议将以下措词纳入第5.2.4.1节
5.2.4.1。实现始终可以自由声明给定程序太大或太复杂而无法翻译或执行。但是,要在没有提供任何有用功能的同时停止要求一致性的一种方法,实现者必须提供一种确定程序是否可能超出限制的方法。该方法不一定是完美的,只要它在谨慎方面出错即可。一种实现方法是拥有一个公式,该公式将诸如变量数之类的值转换为编译器所需的内存量。同样,如果堆栈空间有限制,该公式仅需要显示如何确定每个函数调用的堆栈要求(假设这是分配堆栈的唯一位置),并且不需要遍历所有可能的执行路径(面对递归,这是不可能的)。编译器甚至可以具有为程序中的每个功能输出值的模式。
该实现应能够翻译和执行至少一个程序,该程序包含以下每个限制的至少一个实例: