4

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 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, firstly 1 is passed through the expression a++ and then a is incremented to 2. 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 order 1, 3, 3 get passed. Once printf( ) 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). Thus 3 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?

  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/220146/discussion-on-question-by-antti-haapala-why-does-this-code-print-1-2-2-and-not-t). – Samuel Liew Aug 20 '20 at 14:11
  • 1
    Avoid Yashwant Kanetkar books. Many erroneous examples, infos. – Sreeraj Chundayil Jan 09 '22 at 08:26

4 Answers4

14

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?

Lundin
  • 195,001
  • 40
  • 254
  • 396
5

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:

  • In case the binary operators1) used in the expression don't explicitly state the order that the operands are executed2) and,
  • a side-effect, such as changing the value, happens to a variable in the expression, and,
  • that same variable is used elsewhere in the same expression,

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
3

The author is wrong and the book has multiple instances of incorrect statements like this one.

In C, the behavior of printf("%d %d %d", a, ++a, a++); is undefined both because the order of evaluation of function arguments is unspecified and because modifying the same object multiple times between 2 sequence points has undefined behavior just to name these two.

Note that the book is referenced as do not use in The Definitive C Book Guide and List for providing incorrect advice with this precise example.

Note also that other languages may have a different take on this kind of statement, notably java where the behavior is fully defined.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    @bruno: I agree with the OP that common falsehoods should be corrected whenever possible. Good advice is always valuable. as you can see from majid's answer, the question is not pointless and some incorrect assumptions are deeply rooted. – chqrlie Aug 20 '20 at 14:08
-1

I read many years ago in University (C step by step by Mitchell Waite) which c compiler (some compilers) uses stack for printf by pushing arguments from right to left (to specifier) and then pop them one by one and print them.

I write this code and the output is 3 3 1 : Online Demo.

Based on the book in the stack we have something like this

enter image description here

But after a minor challenges with experts (in comments) I found that maybe in some compilers this sequence be true but not for all.

@Lundin provided this code and the output is 1 2 2 :Online Demo

and @Bob__ provided another example which output is totally different: Online Demo

It totally depends on compiler implementation and has undefined behaviour.

Majid Hajibaba
  • 3,105
  • 6
  • 23
  • 55
  • And what book did you use at the university? – Antti Haapala -- Слава Україні Aug 20 '20 at 13:46
  • C step by step by Mitchell Waite. you can read it and see the online demo result. – Majid Hajibaba Aug 20 '20 at 13:47
  • That book was published in **1989**, before the **first** international standard of C language. – Antti Haapala -- Слава Україні Aug 20 '20 at 13:57
  • Ok so can you or your splendid books please explain to me step by step why clang gives 1 2 2 https://godbolt.org/z/bPEPGW. – Lundin Aug 20 '20 at 14:00
  • Yes! But the first C compilers uses same method as Mitchell Waite said. And in online demo you can see `3 3 1` output. What compiler you use which give `1 2 2`. – Majid Hajibaba Aug 20 '20 at 14:02
  • @Lundin Can you explain me why this give me another result? https://onlinegdb.com/SkVSug3fv – Majid Hajibaba Aug 20 '20 at 14:05
  • @majidhajibaba Oh and also explain to me why icc 19.0.1 for x86-64 gives `3 2 2`. Could it be because undefined behavior is behavior which is not defined? Could it be because bugs don't give deterministic behavior? – Lundin Aug 20 '20 at 14:08
  • @Lundin: your explanations are fine but the tone is inappropriate IMHO. – chqrlie Aug 20 '20 at 14:14
  • @Lundin Some compilers have different behavior, but most of them give 3 3 1 which is works by sequence as I explained. – Majid Hajibaba Aug 20 '20 at 14:16
  • [How to explain undefined behavior to know-it-all newbies?](https://stackoverflow.com/questions/2235457/how-to-explain-undefined-behavior-to-know-it-all-newbies) – Lundin Aug 20 '20 at 14:17
  • 1
    @majidhajibaba: this behavior is not specified by the C Standard. Other compilers can generate code that produce a different result, as a matter of fact the result could differ from one run to another and the compiler could still conform to the C Standard. The behavior is undefined, relying on any specific observed behavior is a mistake, trying to explain the observed behavior is vain. – chqrlie Aug 20 '20 at 14:20
  • 1
    Please note that *"compilers have different behavior"* is a strong hint of undefined behavior: https://godbolt.org/z/PMazGs – Bob__ Aug 20 '20 at 14:21
  • @Lundin , chqrlie , Bob_ OK. you right. But I think many newbies and university student can be wrong like me. Is it better to remove this answer or Not?? – Majid Hajibaba Aug 20 '20 at 14:25
  • 1
    @majidhajibaba: Of course this misconception is quite common and I'm glad you understand why. We all make mistakes and programming in C for 37 years has been a humbling experience. Either remove the answer of rephrase it so it starts with *The author is wrong* an give an explanation about why the author thought it should behave as he writes and why in the C language, this assumption is incorrect and nothing can be assumed about the behavior of this code. – chqrlie Aug 20 '20 at 14:28
  • 3
    Of course everyone can be wrong, student or veterans. But when there are present, detailed answers already posted, maybe take a step back and check those first, see if there's something in them that you had not heard about before (like the term "order of evaluation", many beginner classes fail to teach that one). Yeah it's probably best to delete the answer or you risk attracting a lot of down votes. – Lundin Aug 20 '20 at 14:29
  • @Lundin, Bob__ , chqrlie and Antti Haapala. Thanks for your comments. The post is edited. Please inform it i'm wrong again. – Majid Hajibaba Aug 20 '20 at 15:29
  • 1
    "It totally depends on compiler implementation and has undefined behaviour." These are different things. When something depends on the compiler, the C language calls it _implementation-defined behavior_. Such things are documented by the compiler and work well-defined on that compiler. But this is undefined behavior, meaning that there is no guaranteed result - you can't rely on it and anything might happen. – Lundin Aug 21 '20 at 06:30