Notice: this is a self-Q/A and a more visible targeting the erroneous information promoted by the book "Let us C". Also, please let's keep the c++ out of the discussion, this question is about C.
I am reading the book "Let us C" by Yashwant Kanetkar.
In the book there is the following example:
#include <stdio.h>
int main(void) {
int a = 1;
printf("%d %d %d", a, ++a, a++);
}
The author claims that this code should output 3 3 1
:
Surprisingly, it outputs
3 3 1
. This is because C’s calling convention is from right to left. That is, firstly1
is passed through the expressiona++
and thena
is incremented to2
. Then result of++a
is passed. That is, a is incremented to 3 and then passed. Finally, latest value of a, i.e. 3, is passed. Thus in right to left order1, 3, 3
get passed. Onceprintf
( ) collects them it prints them in the order in which we have asked it to get them printed (and not the order in which they were passed). Thus3 3 1
gets printed.
However when I compile the code and run it with clang
, the result is 1 2 2
, not 3 3 1
; why is that?
The author is wrong. Not only is the order of evaluation of function arguments unspecified in C, the evaluations are unsequenced with regards to each other. Adding to the injury, reading and modifying the same object without an intervening sequence point in independent expressions (here the value of a
is evaluated in 3 independent expressions and modified in 2) has undefined behaviour, so the compiler has the liberty of producing any kind of code that it sees fit.
For details, see Why are these constructs using pre and post-increment undefined behavior?
C’s calling convention
This has nothing to do with calling convention! And C does not even specify a certain calling convention - "cdecl" etc are x86 PC inventions (and have nothing to do with this). The correct and formal C language term is order of evaluation.
The order of evaluation is unspecified behavior (formally defined term), meaning that we can't know if it is left to right or right to left. The compiler need not document it and need not have a consistent order from case to case basis.
But there is a more severe problem yet here: the so-called unsequenced side-effects. C17 6.5/2 states:
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. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.
This text is quite hard to digest for normal humans. A rough, simplified translation from language-lawyer nerd language to plain English:
then the program is broken and might do anything.
1) Operators with 2 operands.
2) Most operators don't do this, only a few exceptions like || && ,
operators do so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With