*p++->str : Understanding evaluation of ->
To understand the expression *p++->str
you need to understand how *p++
works, or in general how postfix increment works on pointers.
In case of *p++
, the value at the location p
points to is dereferenced before the increment of the pointer p
.
n1570 - §6.5.2.4/2:
The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). [...]. The value computation of the result is sequenced before the side effect of updating the stored value of the operand.
In case of *p++->str
, ++
and ->
have equal precedence and higher than *
operator. This expression will be parenthesised as *((p++)->str)
as per the operator precedence and associativity rule.
One important note here is precedence and associativity has nothing to do with the order of evaluation. So, though ++
has higher precedence it is not guaranteed that p++
will be evaluated first. Which means the expression p++
(in the expression *p++->str
) will be evaluated as per the rule quoted above from the standard. (p++)->str
will access the str
member p
points to and then it's value is dereferenced and then the value of p
is incremented any time between the last and next sequence point.
Postfix ++
and ->
have the same precedence. a++->b
parses as (a++)->b
, i.e. ++
is done first.
*p++->str;
executes as follows:
The expression parses as
*((p++)->str)
.->
is a meta-postfix operator, i.e.->foo
is a postfix operator for all identifiersfoo
. Postfix operators have the highest precedence, followed by prefix operators (such as*
). Associativity doesn't really apply: There is only one operand and only one way to "associate" it with a given operator.p++
is evaluated. This yields the (old) value ofp
and schedules an update, settingp
top+1
, which will happen at some point before the next sequence point. Call the result of this expressiontmp0
.tmp0->str
is evaluated. This is equivalent to(*tmp0).str
: It dereferencestmp0
, which must be a pointer to a struct or union, and gets thestr
member. Call the result of this expressiontmp1
.*tmp1
is evaluated. This dereferencestmp1
, which must be a pointer (to a complete type). Call the result of this expressiontmp2
.tmp2
is ignored (the expression is in void context). We reach;
andp
must have been incremented before this point.