Home > other >  behavior of printf( ) statement, in C
behavior of printf( ) statement, in C

Time:12-21

Following are the statements of which I need to find out the output:

int k = 35;
printf("%d %d %d", k == 35, k = 50, k > 40);

Output of the above statements (in gcc compiler):

0 50 0

I ran the above code in VSCode with gcc compiler.

The output I am expecting is:

1 50 1

But, the actual Output is:

0 50 0

Interestingly, if I split the printf( ) statement as follows:

int k = 35;
printf("%d ", k == 35);
printf("%d ", k = 50);
printf("%d ", k > 40);

The actual output is as expected, that is:

1 50 1

So that's why I'm confused with the behavior of printf( ) statement in the original problem. Why the output is "0 50 0" and not "1 50 1" ?

CodePudding user response:

The C standard does not specify any requirements for the behavior of this program. It may print “1 50 1” or “0 50 0”, it may print other outputs, it may abort, or it may do other things.

The argument k = 50 changes the value of k as a side effect. (In an assignment expression, a side effect is to change the value of the left operand. The main effect is that the expression has the new value of the left operand.) The arguments k == 35 and k > 40 compute values using the value of k.

The clause of the C standard that specifies function call behavior, 6.5.2.2, does not specify any sequencing between the arguments of a function call. Then another clause, C 2018 6.5 2, says:

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…

So, this printf call has a side effect on k, which is a scalar object, that is unsequenced relative to value computations using the value of k. So the behavior of the program is not defined by the C standard.

An underlying reason this behavior is not defined by the C standard is that we want compilers to optimize the performance of programs, and this may involve evaluating expressions in parts and executing those parts in any order. For example, if several arguments to a function have a common subexpression, we generally want the compiler to evaluate the common subexpression and use its value multiple times in the several arguments instead of recomputing it each time. So we want to allow the compiler to mix executions of multiple expressions if that improves program performance. It is up to authors of C programs to understand these rules and avoid mixing expressions that can interfere with each other in undesired ways.

Another aspect of this is that some expressions that look like single operations, such as k = 50, might not be. A compiler might support 64-bit integer arithmetic on computer hardware that has only 32-bit operations by using multiple instructions to perform the 64-bit operations. For example, it might perform a 64-bit add by adding the low 32 bits of two numbers, storing the low 32 bits into the result, adding the high 32 bits of the numbers and a carry from the low 32 bits, and storing the high 32 bits into the result. When you combine this fact with the mixing of expression evaluations, it is possible that some parts of an expression like k > 40 (perhaps comparing the high bits) are evaluated before some parts of k = 50 (perhaps storing the low bits) while other parts are evaluated after them. So this means that, when you have arguments like k == a, k = b, k > c, the evaluation of the parts in different orders might produce a result that would actually be impossible if each argument were fully evaluated by itself in any of the six possible orders of three separate things.

  • Related