What is the order of evaluation of assignment operators?

Order of Left and Right Operands

To perform the assignment in arr[global_var] = update_three(2), the C implementation must evaluate the operands and, as a side effect, update the stored value of the left operand. C 2018 6.5.16 (which is about assignments) paragraph 3 tells us there is no sequencing in the left and right operands:

The evaluations of the operands are unsequenced.

This means the C implementation is free to compute the lvalue arr[global_var] first (by “computing the lvalue,” we mean figuring out what this expression refers to), then to evaluate update_three(2), and finally to assign the value of the latter to the former; or to evaluate update_three(2) first, then compute the lvalue, then assign the former to the latter; or to evaluate the lvalue and update_three(2) in some intermixed fashion and then assign the right value to the left lvalue.

In all cases, the assignment of the value to the lvalue must come last, because 6.5.16 3 also says:

… The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands…

Sequencing Violation

Some might ponder about undefined behavior due to both using global_var and separately updating it in violation of 6.5 2, which says:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined…

It is quite familiar to many C practitioners that the behavior of expressions such as x + x++ is not defined by the C standard because they both use the value of x and separately modify it in the same expression without sequencing. However, in this case, we have a function call, which provides some sequencing. global_var is used in arr[global_var] and is updated in the function call update_three(2).

6.5.2.2 10 tells us there is a sequence point before the function is called:

There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call…

Inside the function, global_var = val; is a full expression, and so is the 3 in return 3;, per 6.8 4:

A full expression is an expression that is not part of another expression, nor part of a declarator or abstract declarator…

Then there is a sequence point between these two expressions, again per 6.8 4:

… There is a sequence point between the evaluation of a full expression and the evaluation of the next full expression to be evaluated.

Thus, the C implementation may evaluate arr[global_var] first and then do the function call, in which case there is a sequence point between them because there is one before the function call, or it may evaluate global_var = val; in the function call and then arr[global_var], in which case there is a sequence point between them because there is one after the full expression. So the behavior is unspecified—either of those two things may be evaluated first—but it is not undefined.

Leave a Comment