【问题标题】:Is unevaluated division by 0 undefined behavior?未评估的除以 0 是未定义的行为吗?
【发布时间】:2017-03-03 12:38:18
【问题描述】:

我与一些同事对以下代码存在分歧:

int foo ( int a, int b )
{
    return b > 0 ? a / b : a;
}

此代码是否表现出未定义的行为?

编辑:分歧始于过度渴望优化编译器中的一个错误,其中b > 0 检查已被优化。

【问题讨论】:

  • 有什么理由这么认为吗?
  • 我的意思是当你执行除以0时它只是UB。你不在这里。
  • @LuchianGrigore 我猜他的意思是,如果你的同事(或你)采取这个展示 UB 的立场,也许他们(或你)会这样认为的原因为什么成为您问题的一个有价值的补充。
  • 嗯?当p 为空时是return p ? p->flag_value : false UB?不会。如果是这种情况,所有代码都会被破坏。
  • 如果操作产生溢出也是UB。但是对于只有将 INT_MIN 除以 -1 时才会发生的整数除法,并且保护 (b>0) 也可以避免这种情况。

标签: c++ c language-lawyer


【解决方案1】:

没有。


引自 N4140:

§5.16 [expr.cond]/1

条件表达式从右到左分组。第一个表达式是 上下文转换为布尔值。它被评估,如果它是真的, 条件表达式的结果是第二个的值 表达式,否则为第三个表达式。 只有一个 计算第二个和第三个表达式

进一步:

§5 [expr]/4

如果在计算表达式期间,结果不是 数学定义或不在可表示值的范围内 它的类型,行为未定义。

这显然不会在这里发生。同一段在注释中明确提到除以零,虽然它是非规范性的,但它更清楚地表明它与这种情况有关:

[ 注意:大多数现有的 C++ 实现都忽略了整数溢出。 除以零的处理,使用零形成余数 除数,所有浮点异常因机器而异,并且是 通常可通过库函数进行调整。 ——尾注]


还有间接证据强化了上述观点:条件运算符用于有条件地使行为未定义。

§8.5 [dcl.init]/12.3

int f(bool b) {
  unsigned char c;
  unsigned char d = c; // OK, d has an indeterminate value
  int e = d; // undefined behavior
  return b ? d : 0; // undefined behavior if b is true
}

在上面的示例中,使用d 初始化int(或unsigned char 以外的任何内容)是未定义的。然而,它明确指出只有在评估 UB 分支时才会发生 UB。


从语言律师的角度出发:如果这可能是 UB,那么任何除法都可以被视为 UB,因为除数可能为 0。这不是规则的精神。

【讨论】:

  • @LuchianGrigore 您的问题的答案的本质完全是斜体句子(只评估第二个和第三个表达式中的一个)。如果有人不理解条件操作的该属性,他们可能会产生更多像您这样的问题,其中除以零被取消引用空指针替换,越界访问,预期但未观察到的重要副作用等。
  • -1:问题(如标题中所写)不询问是否对部门进行评估 - 它询问部门是否可以是UB,尽管没有评估。
  • @immibis 确实如此,请参阅 Leon 的评论。
  • @immibis 来自答案中的第二个引用:被零除为UB的原因是因为§5 [expr]/4。请注意“在表达式的求值 期间”和“数学定义”这两个词。第一个引号告诉何时评估“未在数学上定义”表达式,即:从不。
  • @jdm 我假设您在谈到时间旅行时指的是 Raymond Chen 的文章。您会注意到存在的问题是在检查之前调用了 UB。这里的类似物是cout << "a/b: " << a/b << endl; return b != 0 ? a / b : a;
【解决方案2】:

示例代码中没有除零的方法。当处理器执行a / b时,它已经检查了b > 0,因此b不为零。

需要注意的是,如果a == INT_MINb == -1,那么a/b 也是未定义的行为。但这无论如何都会被阻止,因为在这种情况下条件评估为false

虽然我不太确定您的意思是 return b != 0 ? a / b : a; 而不是 return b > 0 ? a / b : a; 如果 b 小于零,除法仍然有效,除非是上述条件。

【讨论】:

  • Hans Olsen 指出了一种情况,在 cmets 中,a / b 仍可能未定义为负 b
【解决方案3】:

此代码是否表现出未定义的行为?

没有。它没有。表达式

return b > 0 ? a / b : a;  

等价于

if(b > 0)
    return a/b;     // this will be executed only when b is greater than 0
else
    return a;  

仅当b 大于0 时才执行除法。

【讨论】:

  • -1:问题(如标题中所写)不询问是否对部门进行评估 - 它询问部门是否可以是UB,尽管没有评估。
  • @immibis; 问题(如标题中所写)不询问是否对除法进行评估:正确,但答案在于对除法表达式的评估。这里没有被零除。
  • 这是一个语句,而不是一个表达式。
  • @Leushenko; a/b 是一个表达式。
【解决方案4】:

如果这是UB,那么也是

if(a != null && *a == 42)
{
 .....
}

并且 ifs 、 ands 和 ors 的排序显然是专门为允许这种类型的构造而设计的。我无法想象你的同事会对此争论

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 2019-09-26
    相关资源
    最近更新 更多