vec[ival++] <= vec[ival]
此表达式具有未定义的行为,因为运算符(<=)操作数的求值顺序未定义。
我们如何重写该表达式以避免未定义的行为? 我找到了一个似乎可行的答案:
vec[ival] <= vec[ival + 1]
如果这是避免未定义行为的正确方法,为什么用这种方式重写它可以避免未定义行为?
添加有关如何修复该表达式的任何参考文献。
答案 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
引入副作用,您只是读取了两次内存位置。
我认为这是解决问题的一种合理方法,它可读且毫不奇怪。一种替代方法可以包括引入第二变量,例如。 index
和prev_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。