you specified gcc
so i will state something very gcc
specific.
this is undefined but if you consider how gcc implements call stack, you will know why it's consistent. basically, stack grows from high address to low address, and function like printf
which has ...
in its argument list will dump the arguments into the stack. given the direction of stack, there aren't too many choices:
func(a1, a2, a3, a4);
push from left to right:
high| ... |
| a1 |
| a2 |
| a3 |
low | a4 |
or right to left:
high| ... |
| a4 |
| a3 |
| a2 |
low | a1 |
the truth is, gcc
takes the second approach. it may sound counter-intuitive, but given such stack construct, this is very nicely designed. if you have to access the arguments in sequence, you naturally form an array in this way, so each of arr[0]
, arr[1]
, etc will correspond to a correct argument in position.
so after knowing this, the evaluation order isn't very hard to comprehend any more, since gcc
handles arguments from right to left any ways. if i need to implement a compiler handles function calls, why would i choose to handle arguments from left to right in this case and right to left in that case? that's more than confusing.
gcc
is a very comprehensive compiler if you know some of its implementation. a technical decision often will drive to a very obvious consequence.
this is very gcc
specific so things could change easily if somebody changes the growth direction of stack or just feel like creating special case. for instance, clang
seems to behave the other way around.