简体   繁体   中英

c++11 Order of evaluation (undefined behavior)

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

This expression has undefined behavior, because the order of evaluation of operator's (<=) operands is undefined.

How can we rewrite that expression to avoid the undefined behavior? I've found an answer that appears to work:

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

If that is the right way to avoid the undefined behavior, why does rewriting it that way avoid the undefined behavior?

Adding any reference about how to fix that expression would be great.

Yes, your first example has undefined behavior because we have an unsequenced modification and access of a memory location, which is undefined behavior. This is covered in draft C++ standard [intro.execution]p10 ( emphasis mine ):

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced . [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a memory location ([intro.memory]) is unsequenced relative to either another side effect on the same memory location or a value computation using the value of any object in the same memory location, and they are not potentially concurrent ([intro.multithread]), the behavior is undefined . [ Note: The next subclause imposes similar, but more complex restrictions on potentially concurrent computations. — end note ] [ Example:

 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 } 

— end example ]

If we check out the section of relational operators which covers <= [expr.rel] it does not specify an order of evaluation, so we are covered by intro.exection and thus we have undefined behavior.

Having unspecified order of evaluation is not sufficient for undefined behavior as the example in Order of evaluation of assignment statement in C++ demonstrates.

Your second example avoids the undefined behavior since you are not introducing a side effect to ival , you are just reading the memory location twice.

I believe that is a reasonable way to solve the problem, it is readable and not surprising. An alternate method could include introducing a second variable, eg index and prev_index . It is hard to come with a fast rule given such a small code snippet.

It avoids undefined behavior because you are not changing the value of ival . The issue you're seeing in the first sample is that we can't determine what the values of ival are at the times that they're used. In the second sample, there's no confusion.

Let's start with the worst problem first, and that is the Undefined Behavior. C++ uses sequencing rules. Statements are executed in seqeuence. Usually that's top-to-bottom, but if statements, for statements, function calls and the like can change that.

Now withing a statement there still might be a further sequence of execution, but I'm very intentionally writing might . By defaults, the various parts of a single statement are not sequenced relative to each other. That's why you can get the varying order of execution. But worse, if you change and use a single object without sequencing, you have Undefined Behavior. That is bad - anything might happen.

The proposed solution ( iVal + 1 ) doesn't change iVal anymore, but generates a new value. That is entirely safe. Still, it leaves iVal unchanged.

You may want to check on std::adjacent_find() . Chances are that the loop you're trying to write is already in the Standard Library.

The first problem is that as the initial code exhibited undefined behavior, under the C++ standard there is no "fix". The behavior of that line of code is not specified by the C++ standard; to know what it is supposed to do you have to have another source of information.

Formally, that expression can be rewritten as system("format c:") , as the C++ standard does not mandate any behavior from a program that exhibits undefined behavior.

But in practice, when you run into something like that, you have to read the original programmer's mind.

Well, you can solve anything with lambdas.

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

Second,

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

is not the same, because it lacks the side effect of ival being 1 greater after the expression is evaluated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM