How does an equal to expression work in a printf placeholder? [duplicate]
Asked Answered
E

1

1

I have the following code snippet:

main( )
{
int k = 35 ;
printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ;
}

which produces the following output

0 50 0

I'm not sure I understand how the first value of the printf comes to 0. When the value of k is compared with 35, it should ideally return (and thus print) 1, but how is it printing zero? The other two values that are produced- 50 and 0 are all right, because in the second value, the value of k is taken as 50, and for the third value- the value of k(which is 35) is compared with 40. Since 35 < 40, so it prints 0.

Any help would be appreciated, thanks.

**UPDATE**

After researching more on this topic and also on undefined behavior, I came across this in a book on C, source is given at the end.

Calling Convention Calling convention indicates the order in which arguments arepassed to a function when a function call is encountered. There are two possibilities here:

  1. Arguments might be passed from left to right.
  2. Arguments might be passed from right to left.

C language follows the second order.

Consider the following function call:

fun (a, b, c, d ) ;

In this call it doesn’t matter whether the arguments are passed from left to right or from right to left. However, in some function call the order of passing arguments becomes an important consideration. For example:

int a = 1 ;
printf ( "%d %d %d", a, ++a, a++ ) ;

It appears that this printf( ) would output 1 2 3. This however is not the case. 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.

**Source: "Let Us C" 5th edition, Author: Yashwant Kanetkar, Chapter 5: Functions and Pointers**

Regardless of whether this question is a duplicate or not, I found this new information to be helpful to me, so decided to share. Note: This also supports the claim presented by Mr.Zurg in the comments below.

Euler answered 21/7, 2014 at 13:42 Comment(7)
Good question, if you don't know the terms hard to find though ... it is Undefined behavior, the comma in a function call does not introduce a sequence point. Related but not a dup of Why are these constructs undefined behavior?Theatrical
This is probably a close enough dup: Is the output of printf (“%d %d”, c++, c); also undefined?Theatrical
…or mark this as a duplicate; the only thing, OP needs to know is the keyword "sequence point".Breakfront
Undefined Behaviour and Sequence Points has an example of this, though the overall question is C++.Aubigny
@Aubigny I was never really happy with the answers to that question, not really clear how to improve them w/o a complete revamp but no real room to make it worth adding a new answer.Theatrical
"C language follows the second order" -- If that's a quote from the book, throw the book away.Miscue
@DarkKnight the C standard is a better authority on C than Yashwant Kanetkar isZephyr
T
7

Unfortunately for all of those who read that book it is totally wrong. The draft C99 standard clearly make this code undefined behavior. A quick check with the Wikipedia entry on undefined behavior contain a similar example and identifies it as undefined. I won't leave it at that but there are other easily accessible sources that get this right without having to resort to the standard.

So what does the standard say? In section 6.5 Expressions paragraph 3 says:

The grouping of operators and operands is indicated by the syntax.74) Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.

So unless specified the order of evaluation of sub-expressions is unspecified, and further section 6.5.2.2 Function calls paragraph 10 says:

The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

So in your example:

printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ;
         ^             ^        ^       ^
         1             2        3       4

sub-expression 1 to 4 could be evaluated in any order and we have no way of knowing when the side effects from each sub-expression will take place although we know they all have to take effect before the function is actually called.

So k = 50 could be evaluated first or last or anywhere in between and regardless of when it is evaluated the side effect of changing the value of k could take place immediately after or not until the actual function is executed. Which means the results is unpredictable could conceivably change change during different executions.

So next we have to tackle how this becomes undefined behavior. This is covered in section 6.5 paragraph 2 which says:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.72) Furthermore, the prior value shall be read only to determine the value to be stored.73)

So in your example we are not modifying k more than once but we are using the prior value to do other things besides determine the value to be stored. So what are the implications of undefined behavior? In the definition of undefined behavior the standard says:

Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

So the compiler could ignore it or could also produce unpredictable results, which covers a pretty wide range of undesirable behaviors. It has infamously been said that:

When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose

which sounds pretty undesirable to me.

Theatrical answered 24/7, 2014 at 2:29 Comment(3)
Is it also UB in C11?Zephyr
@MattMcNabb yes it is undefined in C11 although the logic to get there is slightly different.Theatrical
In C11 it's quite straightforward, evaluation of the function arguments are unsequenced, and "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." (6.5/2)Boulevard

© 2022 - 2024 — McMap. All rights reserved.