How does an expression equal to an expression work in the printf placeholder?

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 conclusion

 0 50 0 

I'm not sure I understand how the first printf value comes to 0 . When the value of k compared to 35 , it should ideally return (and therefore print) 1, but how does it print 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 to 40 . Since 35 < 40 , so it prints 0.

Any help would be appreciated, thanks.

** UPDATE **

After a more detailed study of this topic, as well as on undefined behavior , I came across this in a book in C, the source code is given at the end.

Calling a convention A calling convention indicates the order in which arguments are passed to a function when the function is called. There are two possibilities:

  • Arguments can be passed from left to right.
  • Arguments can be passed from right to left.

The C language follows the second order.

Consider the following function call:

 fun (a, b, c, d ) ; 

In this call, it does not matter whether the arguments are passed left to right or right to left. However, in some function call, the order in which the arguments pass becomes an important consideration. For instance:

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

It looks like this printf( ) outputting 1 2 3 . However, it is not. Surprisingly, it outputs 3 3 1 .

This is because the Cs calling convention is from right to left . That is, firstly, 1 is passed through the expression a++ , and then a increases by 2 . Then the result is ++a . That is, a increases to 3 , and then it is transmitted. Finally, the last value of a , i.e. 3 is transmitted. So in right to left order 1, 3, 3 is passed. As soon as printf( ) collects them, it prints them in the order in which we asked him to print them (and not the order in which they were transferred). Thus, 3 3 1 is 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 useful to me, so I decided to share it. Note. This also confirms the application submitted by Mr. Zurg in the comments below.

+1
source share
1 answer

Unfortunately, for all those who have read this book, this is completely wrong. a draft C99 standard explicitly executes this undefined code. A quick check using Wikipedia for undefined behavior contains a similar example and identifies it as undefined. I will not leave it, but there are other easily accessible sources that get this right without resorting to the standard.

So what does the standard say? Section 6.5 in paragraph 3 of the expressions says:

The grouping of operators and operands is denoted by the syntax .74) Except as noted below (for the-call () function, & &, ||,?: And commas), the order in which subexpressions are evaluated and the order in which side effects occur are not defined.

Therefore, if not specified, the procedure for evaluating subexpressions is not specified, and then section 6.5.2.2 functions in paragraph 10:

The order of evaluation of the function pointer, the actual arguments and the subexpressions in the actual arguments is undefined, 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 can be evaluated in any order, and we cannot know when the side effects of each subexpression will occur, although we know that they all need to take effect before the function is actually called.

So, k = 50 can be evaluated both the first and the last, or somewhere between them, and regardless of when it is evaluated, a side effect of changing the value of k can take place immediately after or until the actual function will be executed. This means that the results are unpredictable, perhaps they can change the change during different performances.

So we have to decide how this becomes undefined. This is described in paragraph 6.5 , which reads:

Between the previous and the next point in the sequence, the object must have its expression changed no more than once by evaluating .72) In addition, the previous value should be read only to determine the value that needs to be saved .73)

So, in your example, we do not change k several times, but we use the previous value to do other things besides defining the value that needs to be saved. So what are the consequences of undefined behavior? The undefined behavior definition in the standard says:

Possible undefined behavior varies from ignoring the situation completely with unpredictable results, behaving during the translation or execution of the program in a documented manner specific to (with or without a diagnostic message), to stop the translation or execution (with the issuing of a diagnostic message).

Thus, the compiler can ignore it or it can also produce unpredictable results that cover a fairly wide range of unwanted actions. It was shameful to say that:

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

which sounds pretty undesirable to me.

+7
source

Source: https://habr.com/ru/post/986817/


All Articles