未计算除以 0 是未定义的行为吗?

Is unevaluated division by 0 undefined behavior?

提问人:Luchian Grigore 提问时间:10/21/2016 最后编辑:Luchian Grigore 更新时间:11/21/2016 访问量:4024

问:

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

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

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

编辑:分歧始于一个过于急切的优化编译器中的一个错误,其中检查被优化了。b > 0

C++ C 语言律师

评论

16赞 juanchopanza 10/21/2016
有什么理由认为它确实如此吗?
11赞 Hayt 10/21/2016
我的意思是,只有当你执行除以 0 时,它才是 UB。你不在这里。
23赞 WhozCraig 10/21/2016
@LuchianGrigore我猜他的观点是,如果你的同事(或你)采取这种立场,那么他们(或你)这么认为的原因可能是对你的问题的一个有价值的补充。
45赞 Kerrek SB 10/21/2016
哼?UB什么时候是null吗?哈哈如果是这样的话,所有代码都会被破坏。return p ? p->flag_value : falsep
7赞 Hans Olsson 10/21/2016
如果操作生成溢出,它也将是 UB。但是对于整数除法,这只有在将 INT_MIN 除以 -1 时才会发生,而守卫 (b>0) 也避免了这种情况。

答:

111赞 krzaq 10/21/2016 #1

哈哈


来自 N4140 的报价:

§5.16 [expr.cond]/1

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

进一步:

§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
}

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


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

评论

25赞 Leon 10/21/2016
@LuchianGrigore 您的问题答案的本质完全在斜体句子中(仅评估第二和第三种表达中的一种)。如果有人不理解条件操作的属性,他们可能会产生更多像你这样的问题,其中除以零被取消引用空指针、越界访问、预期但未观察到的重要副作用等所取代。
1赞 user253751 10/22/2016
-1:问题(如标题中所写)不问是否对除法进行了评估 - 它问的是尽管没有经过评估,但除法是否可以是UB。
1赞 Ruslan 10/22/2016
@immibis确实如此,请参阅 Leon 的评论。
1赞 milleniumbug 10/22/2016
@immibis 从答案中的第二句话可以看出:除以零是UB的原因是。请注意“在表达式的计算过程中”和“数学定义”这两个词。第一个引号表示何时计算“未数学定义”表达式,即:从不。§5 [expr]/4
1赞 krzaq 10/24/2016
@jdm 我猜你在谈到时间旅行时指的是 Raymond Chen 的文章。您会注意到,问题在于在检查之前调用了 UB。这里的类似物是 .cout << "a/b: " << a/b << endl; return b != 0 ? a / b : a;
15赞 glauxosdever 10/21/2016 #2

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

应该注意的是,if 和 ,then 也是未定义的行为。但无论如何,这都是可以防止的,因为在这种情况下,条件的评估结果为。a == INT_MINb == -1a/bfalse

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

评论

2赞 chepner 10/21/2016
汉斯·奥尔森(Hans Olsen)指出了一种情况,在评论中仍然可以未定义负面。a / bb
10赞 haccks 10/22/2016 #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;  

仅当大于 时才执行除法。b0

评论

1赞 user253751 10/22/2016
-1:问题(如标题中所写)不问是否对除法进行了评估 - 它问的是尽管没有经过评估,但除法是否可以是UB。
2赞 haccks 10/22/2016
@immibis;问题(如标题中所写)不问是否对除法进行评估:对,但答案在于对除法表达式的评估。这里没有除以零。
0赞 Alex Celeste 10/22/2016
这是一个声明,而不是一个表达。
1赞 haccks 10/23/2016
@Leushenko; 是一个表达式。a/b
3赞 pm100 10/27/2016 #4

如果这是UB,那么也会

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

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