调用未定义行为(在本例中,被零除)的代码永远不会执行,程序仍然是未定义行为吗?
int main(void)
{
int i;
if(0)
{
i = 1/0;
}
return 0;
}
我认为这仍然是未定义的行为,但我在标准中找不到任何支持或否定我的证据.
有什么 idea 吗?
调用未定义行为(在本例中,被零除)的代码永远不会执行,程序仍然是未定义行为吗?
int main(void)
{
int i;
if(0)
{
i = 1/0;
}
return 0;
}
我认为这仍然是未定义的行为,但我在标准中找不到任何支持或否定我的证据.
有什么 idea 吗?
让我们看看C标准如何定义术语"行为"和"未定义的行为".
参考ISO C 2011标准N1570草案;我不知道三个已发布的ISO C标准(1990年、1999年和2011年)中有任何相关差异.
第3.4节:
behavior
好吧,这有点模糊,但我认为给定的语句没有"外观",当然也没有"动作",除非它实际执行.
第3.4.3节:
undefined behavior
行为,在使用不可移植或错误的程序构造或错误的数据时, 本国际标准对此没有任何要求
上面写着这样一个 struct 的"upon use".标准中没有定义"使用"这个词,所以我们回到了普通的英语意思.如果一个构造从未执行过,它就不会被"使用".
在这个定义下有一条注释:
注意:可能的未定义行为包括忽略情况
因此,如果程序的行为未定义,编译器可以拒绝程序at compile time.但我的解释是,如果它能证明程序的每次执行都会遇到未定义的行为,它就可以这样做.我认为这意味着:
if (rand() % 2 == 0) {
i = i / 0;
}
它当然有未定义的行为,不能在编译时被拒绝.
实际上,程序必须能够执行运行时测试,以防止调用未定义的行为,标准必须允许它们这样做.
你的例子是:
if (0) {
i = 1/0;
}
它从不执行0的除法.一个非常常见的成语是:
int x, y;
/* set values for x and y */
if (y != 0) {
x = x / y;
}
如果为y == 0
,除法当然有未定义的行为,但如果为y == 0
,则永远不会执行除法.行为是定义良好的,原因与您的示例定义良好的原因相同:因为potential个未定义的行为永远不会实际发生.
(除非是INT_MIN < -INT_MAX && x == INT_MIN && y == -1
(是的,整数除法可能会溢出),但这是另一个问题.)
在一条 comments 中(自删除后),有人指出编译器可能会在编译时计算常量表达式.这是正确的,但与本案无关,因为在
i = 1/0;
1/0
is not a constant expression.
constant-expression是一个语法类别,减少到conditional-expression(不包括赋值和逗号表达式).结果constant-expression出现在语法only中,实际需要常量表达式的上下文中,例如大小写标签.所以如果你写:
switch (...) {
case 1/0:
...
}
那么1/0
是一个常量表达式——它违反了6.6p4中的约束:"每个常量表达式的计算结果都应在可表示的范围内
(看起来完全像constant-expression的东西不一定是constant-expression,正如在x + y * z
中,序列x + y
不是additive-expression,因为它出现在上下文中.)
这意味着N1570第6.6节中我要引用的脚注:
Thus, in the following initialization,
static int i = 2 || 1 / 0;
the expression is a valid integer constant expression with value one.
实际上与这个问题无关.
最后,有几件事被定义为导致未定义的行为,这些行为与执行过程中发生的事情无关.附件J,C标准的第2节(同样,参见第N1570 draft部分)列出了从该标准的睡觉收集的导致未定义行为的内容.下面是一些例子(我不认为这是一个详尽的列表):
- 非空源文件不会以新行字符结尾,而新行字符之前不会紧跟反斜杠字符,也不会以部分字符结尾
- 标记串联会生成与通用字符名称的语法匹配的字符序列
- 除标识符、字符常量、字符串之外,源文件中会遇到不在基本源字符集中的字符 文本、标头名称、注释或符合以下条件的预处理标记 从未转换为令牌
- 标识符、注释、字符串文字、字符常量或标头名称包含无效的多字节字符或没有开头
- 同一标识符在同一翻译单元中具有内部和外部链接
这些特定情况是编译器could检测到的情况.我认为他们的行为是未定义的,因为委员会不想或不能将相同的行为强加给所有实现,并且定义一系列允许的行为是不值得的.它们实际上并不属于"永远不会执行的代码"这一类,但我在这里提到它们是为了完整性.