c ++ 11评估顺序(未定义的行为)

时间:2018-10-30 13:46:03

标签: c++ c++11 undefined-behavior

vec[ival++] <= vec[ival]

此表达式具有未定义的行为,因为运算符(<=)操作数的求值顺序未定义。

我们如何重写该表达式以避免未定义的行为? 我找到了一个似乎可行的答案:

vec[ival] <= vec[ival + 1]

如果这是避免未定义行为的正确方法,为什么用这种方式重写它可以避免未定义行为?

添加有关如何修复该表达式的任何参考文献。

4 个答案:

答案 0 :(得分:2)

是的,您的第一个示例具有未定义的行为,因为我们对存储位置进行了未排序的修改和访问,这是未定义的行为。 C ++标准草案[intro.execution]p10强调我的)中对此进行了介绍:

  

除非另有说明,否则对单个运算符的操作数进行评估   以及单个表达式的子表达式是无序列的。   [注意:在表达式中,在   程序的执行,无序和不确定的顺序   对其子表达式的评估不必始终执行   在不同的评估中。 —注释note]的值计算   运算符的操作数先于的值计算进行排序   运算符的结果。 如果对存储位置有副作用   ([intro.memory])相对于另一种副作用没有顺序   在相同的存储位置或使用   同一存储位置中的任何对象,并且它们不可能   并发([intro.multithread]),则行为未定义。 [ 注意:   下一个条款对类似内容施加了类似但更复杂的限制   潜在的并发计算。 —注释] [示例:

void g(int i) {
  i = 7, i++, i++;              // i becomes 9

  i = i++ + 1;                  // the value of i is incremented
  i = i++ + i;                  // the behavior is undefined
  i = i + 1;                    // the value of i is incremented
}
     

-示例example]

如果我们检查关系运算符中涵盖<= [expr.rel]的部分,则它没有指定评估顺序,因此我们被intro.exection所覆盖,因此我们具有未定义的行为。

Order of evaluation of assignment statement in C++中的示例所示,没有指定的评估顺序不足以解决不确定的行为。

您的第二个示例避免了未定义的行为,因为您没有给ival引入副作用,您只是读取了两次内存位置。

我认为这是解决问题的一种合理方法,它可读且毫不奇怪。一种替代方法可以包括引入第二变量,例如。 indexprev_index。给出这么小的代码段,很难有一个快速的规则。

答案 1 :(得分:0)

它避免了不确定的行为,因为您没有更改ival的值。您在第一个示例中看到的问题是,我们无法确定ival的值在使用时的含义。在第二个示例中,没有混淆。

答案 2 :(得分:0)

让我们首先从最严重的问题开始,那就是未定义的行为。 C ++使用排序规则。语句按顺序执行。通常这是从上到下的,但是builder ->语句,if语句,函数调用等可以改变这种情况。

现在,即使有一条语句,仍然可能会有进一步的执行顺序,但是我非常有意地编写可能。默认情况下,单个语句的各个部分不相对于彼此排序。这就是为什么您可以获得不同的执行顺序的原因。但是更糟糕的是,如果您更改和使用单个对象而不进行排序,那么您将拥有未定义的行为。那很糟糕-可能发生任何事情。

建议的解决方案(for)不再更改iVal + 1,而是生成了一个新值。那是完全安全的。尽管如此,它仍然iVal不变。

您可能要检查iVal。可能是您要编写的循环已经在标准库中。

答案 3 :(得分:0)

第一个问题是,由于初始代码表现出不确定的行为,因此在C ++标准下没有“修复”。 C ++标准未指定该行代码的行为。要知道该怎么办,您必须拥有另一个信息来源。

从形式上讲,该表达式可以重写为system("format c:"),因为C ++标准不强制程序表现出未定义的行为。

但是实际上,当您遇到类似问题时,您必须阅读原始程序员的想法。

好吧,您可以使用lambda解决任何问题。

[&]{ bool ret = vec[ival] <= vec[ival+1]; ++ival; return ret; }()

第二,

vec[ival] <= vec[ival+1]

是不一样的,因为它没有ival的副作用,该副作用在表达式被求值后要大1。