cundefined-behaviorc23

Is there a sequence point between the evaluation of a function's arguments and the assignment of the return value of a function to another object?


Is there a sequence point between the evaluation of a function's arguments and the assignment of the return value of a function to another object?

in other words, is this well defined behavior?

int x = 0;
x += func(++x);

I know that there's a sequence point between the evaluation of the function designator and the function arguments, as well as between the function arguments and the operations within the function, but what about between the function arguments and the assignment of a variable based on the return value of the function itself?

The way the standard is written doesn't give a clear answer to this, and different LLMs give different responses as well.

from the N3096 draft of the C23 standard:

The following are the sequence points described in 5.1.2.3:

  • Between the evaluations of the function designator and actual arguments in a function call and the actual call. (6.5.2.2).
  • Between the evaluations of the first and second operands of the following operators: logical AND&&(6.5.13); logical OR || (6.5.14); comma , (6.5.17).
  • Between the evaluations of the first operand of the conditional ?: operator and whichever of the second and third operands is evaluated (6.5.15).
  • Between the evaluation of a full expression and the next full expression to be evaluated. The following are full expressions: a full declarator for a variably modified type; an initializer that is not part of a compound literal (6.7.10); the expression in an expression statement (6.8.3); the controlling expression of a selection statement (if or switch) (6.8.4); the controlling expression of a while or do statement (6.8.5); each of the (optional) expressions of a for statement (6.8.5.3); the (optional) expression in a return statement (6.8.6.4).
  • Immediately before a library function returns (7.1.4).
  • After the actions associated with each formatted input/output function conversion specifier (7.23.6, 7.31.2).
  • Immediately before and immediately after each call to a comparison function, and also between any call to a comparison function and any movement of the objects passed as arguments to that call (7.24.5).

Solution

  • This is in fact undefined behavior, due to the use of a compound assignment operator.

    An expression of the form a += b is exactly the same as a = a + b, except that a is only evaluated once. That means that this:

    x += func(++x);
    

    Is essentially the same as this:

    x = x + func(++x);
    

    And looking at the above, it's clear that the evaluation of x on the left and the side effect of the ++ operator applied to x as a function parameter are unsequenced with respect to each other.

    This also means that the following is not undefined behavior:

    x = func(++x);
    

    Due to the sequence point introduced by the call to the function func and by the fact that the evaluation of the left side of the assignment doesn't actually use the value.