The order of evaluation is unspecified for OPERANDS of an operator.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.
you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.
(p = (p->next = q))
Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next
No, you are assuming that precedence and associativity forces order of
operation again, and even more importantly that it forces order of
modification of objects.
The expression p = p->next = q; could be executed as:
1. Evaluate the value of 'q'.
2. If an automatic conversion is available and necessarily, convert
'q' to the type of 'p->next', and this value, after the conversion if
any, is the value that will be assigned to p->next.
3. Store the value unchanged or converted from step 2 into 'p', with
a possible further conversion if the type of 'p' is different from the
type of 'p->next'.
4. Evaluate 'p' (the new value, equal to 'q') and dereference it to
store the value unchanged or converted from step to into 'p->next'.
To put it more concretely, let's assume a type:
struct obj { struct obj *next };
And two objects of this type:
struct obj o1, o2;
And two pointers to objects of this type:
struct obj *p = &o1, *q = &o2;
You are making assertions about results after the sequence point at
the end of the statement:
p = p->next = q;
....namely:
1. 'p' and 'q' will be identical, both will point to 'o2', neither
will point to 'o1'. This is true.
2. 'o1.next' will point to 'o2' and 'o2.next' will still be
uninitialized if it has automatic storage duration, or NULL if it has
static storage duration, and this is NOT guaranteed by the C standard.
The compiler could follow the four steps I outlined above:
1. Evaluate the lvalue 'q' to its contents, an address of type
'pointer to struct obj' (actual value &02).
2. Evaluate the TYPE of p->next, and since it is also 'pointer to
struct obj', leave the value from step 1 unchanged.
3. Store this value (&o2) into 'p', where again it needs no
conversion.
4. Evaluate 'p' (now containing &o2 instead of the original &o1) and
store the value from step 1, which needed no conversions, into
'p->next'.
I am not saying that any given compiler would actually execute that
statement in that sequence, but THERE IS NOTHING IN THE C STANDARD
THAT PREVENTS IT.
You are expecting o1.next to contain the address of o2, but a compiler
that did not modify o1.next and instead assigned o2.next with the
address of o2 WOULD BE COMPLETELY CONFORMING.
And that is because the evaluation of 'p->next' is NOT required to
compute the new value to be assigned to 'p'. The assignment operators
in C most specifically do NOT store a value into an object, then read
that value back, to yield the value of the expression.