调用未定义行为(在本例中,被零除)的代码永远不会执行,程序仍然是未定义行为吗?

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/0is 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检测到的情况.我认为他们的行为是未定义的,因为委员会不想或不能将相同的行为强加给所有实现,并且定义一系列允许的行为是不值得的.它们实际上并不属于"永远不会执行的代码"这一类,但我在这里提到它们是为了完整性.

C++相关问答推荐

有关字符数组指针和次指针以及qsort函数中的cmp函数的问题

初始化char数组-用于初始化数组的字符串是否除了存储数组的位置之外单独存储在内存中

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

不同到达时间的轮询实现

在C中使用动态内存分配找到最小的负数

在C语言中,在数学运算过程中,为什么浮点数在变量中的行为不同

二进制计算器与gmp

加密解密工作正常,但返回错误0x80090005

用gcc-msse 2编译的C程序包含AVX 1指令

FRIDA-服务器成为端口扫描的目标?

在移动数组元素时获得意外输出

Caesar密码调试:输出文本末尾的问号和随机字符

-Wnonnull-Compare警告不是具有误导性吗?

GCC错误,共享内存未定义引用?

使用Open62541向OPCUA服务器发送读请求时内存泄漏

从C中的函数返回静态字符串是不是一种糟糕的做法?

从另一个宏函数调用C宏

当另一个指向 const 的指针观察到数据时,通过指针更改数据是否安全?

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?

为什么 C 字符串并不总是等同于字符数组?