gpt4 book ai didi

永远不会执行的代码可以调用未定义的行为吗?

转载 作者:行者123 更新时间:2023-12-01 05:48:37 25 4
gpt4 key购买 nike

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

int main(void)
{
int i;
if(0)
{
i = 1/0;
}
return 0;
}

我认为这仍然是未定义的行为,但我在标准中找不到任何证据来支持或否认我。

那么,有什么想法吗?

最佳答案

让我们看看 C 标准如何定义术语“行为”和“未定义行为”。

引用的是 ISO C 2011 标准的 N1570 草案;我不知道三个已发布的 ISO C 标准(1990、1999 和 2011)中的任何一个有任何相关差异。

第 3.4 节:

behavior
external appearance or action



好吧,这有点含糊,但我认为给定的语句没有“外观”,当然也没有“ Action ”,除非它被实际执行。

第 3.4.3 节:

undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements



它说“使用时”这样的构造。标准中没有定义“use”这个词,所以我们回到常见的英语含义。如果从未执行过构造,则不会“使用”它。

该定义下有一个注释:

NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).



因此,如果程序的行为未定义,则允许编译器在编译时拒绝您的程序。但我对此的解释是,只有当它能够证明程序的每次执行都会遇到未定义的行为时,它才能这样做。我认为,这意味着:
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 ,它永远不会执行。行为定义明确,与您的示例定义明确的原因相同:因为潜在的未定义行为永远不会真正发生。

(除非 INT_MIN < -INT_MAX && x == INT_MIN && y == -1 (是的,整数除法会溢出),但这是一个单独的问题。)

在评论中(自删除​​后),有人指出编译器可能会在编译时评估常量表达式。这是真的,但在这种情况下不相关,因为在
i = 1/0;
1/0 不是常量表达式。

常量表达式是一种语法类别,可简化为条件表达式(不包括赋值和逗号表达式)。产生式常量表达式仅在实际需要常量表达式的上下文中出现在语法中,例如案例标签。所以如果你写:
switch (...) {
case 1/0:
...
}

那么 1/0 是一个常量表达式——并且违反了 6.6p4 中的约束:“每个常量表达式都应计算为一个在可表示范围内的常量
其类型的值。”,因此需要进行诊断。但赋值的右侧不需要常量表达式,仅需要条件表达式,因此对常量表达式的约束不适用。编译器可以评估它能够在编译时执行的任何表达式,但前提是行为与在执行期间评估的行为相同(或者,在 if (0) 的上下文中,不在 execution() 期间评估)。

(看起来完全像常量表达式的东西不一定是常量表达式,就像在 x + y * z 中,序列 x + y 不是附加表达式,因为它出现的上下文。)

这意味着我要引用的 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 )列出了从标准的其余部分收集的导致未定义行为的事物。一些例子(我不认为这是一个详尽的 list )是:

  • A nonempty source file does not end in a new-line character which is not immediately preceded by a backslash character or ends in a partial preprocessing token or comment
  • Token concatenation produces a character sequence matching the syntax of a universal character name
  • A character not in the basic source character set is encountered in a source file, except in an identifier, a character constant, a string literal, a header name, a comment, or a preprocessing token that is never converted to a token
  • An identifier, comment, string literal, character constant, or header name contains an invalid multibyte character or does not begin and end in the initial shift state
  • The same identifier has both internal and external linkage in the same translation unit


这些特殊情况是编译器可以检测到的。我认为他们的行为是未定义的,因为委员会不想或不能将相同的行为强加给所有实现,并且定义一系列允许的行为是不值得的。它们并不真正属于“永远不会执行的代码”的范畴,但我在这里提到它们是为了完整性。

关于永远不会执行的代码可以调用未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58661493/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com