Quick doubt!

#include <stdio.h>
int main()
{
int y=10;
int z=(++y * ((y++) +5));
printf(“Hello World %d”,z);

return 0;

}

Value of z? and how?

it will be 192. Firstly this part will be evaluated - > ((y++)+5) since u put a bracket in it which evaluates to 16 and then this result 16 will be multiplied with 12 resulting in 192

That doesn’t seem very correct. Here’s a code demonstrating the first part of your process of evaluation:

#include <stdio.h>

int main (void) {
	int y = 10;
	int z = ((y++) + 5);
	printf("y: %d\nz: %d\n", y, z);
	return 0;
}

which produces output:

y: 11
z: 15

Clearly, that does not evaluate to 16. :slightly_smiling_face:

1 Like

Then what do you think is the correct method??

From the way you wrote code, the value of z depends on whether ++y or ((y++) + 5) gets evaluated first to compute their product. This has not been defined in the original language definition of C.

For your reference, see pg 77 of ‘The C Programming Language’.


Here, the push function pushes a value that depends on whether the first pop gets executed first or not.

This is similar to your case. However, I’m using gcc compiler and the output it produces is 176.
This should be straight forward. As ++y gets evaluated first, the value of y will be 11. The value of ((y++) + 5) will be 11 + 5 = 16. So, the value of z will be 11 * 16 = 176. y++ will be done after assigning value to z. So, by the end, y will be 12 and z will be 176.

2 Likes

Firstly, you’ve got to enclose your code in a pair of ```'s in case you don’t want to hurt the eyes of the reader.

Secondly, never write such a horrible code as it leads to undefined behaviour. However, here’s my evaluation.

The value of z I’m getting is 192.

Reason

I’ve used gcc with the -S flag to generate the assembly. Here’s the content of the resultant file.

	.file	"prog.c"
	.text
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC0:
	.ascii "Hello World %d\0"
	.text
	.globl	_main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB12:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	andl	$-16, %esp
	subl	$32, %esp
	call	___main
	movl	$10, 28(%esp)
	addl	$1, 28(%esp)
	movl	28(%esp), %eax
	leal	1(%eax), %edx
	movl	%edx, 28(%esp)
	leal	5(%eax), %edx
	movl	28(%esp), %eax
	imull	%edx, %eax
	movl	%eax, 24(%esp)
	movl	24(%esp), %eax
	movl	%eax, 4(%esp)
	movl	$LC0, (%esp)
	call	_printf
	movl	$0, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE12:
	.ident	"GCC: (GNU) 7.4.0"
	.def	_printf;	.scl	2;	.type	32;	.endef

Now I’m going to show you what lines 4 - 6 translates to in assembly down below. I’ve explained every step symbolically so that it becomes easy for you to understand, assuming that you don’t have much experience with AT&T assembly language.

NOTE

  1. LC0 points to the character sequence "Hello World %d\0"
  2. is the assignment operator
  3. * is the multiplication operator
	Instruction				;	Action performed		;	State of variables
							;							;
	movl	$10, 28(%esp)	;	y ← 10					;	y = 10
	addl	$1, 28(%esp)	;	y ← y + 1				;	y = 11
	movl	28(%esp), %eax	;	eax ← y					;	eax = 11, y = 11
	leal	1(%eax), %edx	;	edx ← eax + 1			;	edx = 12, eax = 11, y = 11
	movl	%edx, 28(%esp)	;	y ← edx					;	y = 12, edx = 12, eax = 11
	leal	5(%eax), %edx	;	edx ← eax + 5			;	edx = 16, y = 12, eax = 11
	movl	28(%esp), %eax	;	eax ← y					;	eax = 12, edx = 16, y = 12
	imull	%edx, %eax		;	eax ← edx * eax			;	eax = 192, edx = 16, y = 12
	movl	%eax, 24(%esp)	;	z ← eax					;	z = 192, eax = 192, edx = 16, y = 12
	movl	24(%esp), %eax	;	eax ← z					;	eax = 192, z = 192, edx = 16, y = 12
	movl	%eax, 4(%esp)	;	_printf.arg[1] ← eax	;	_printf.arg[1] = 192, eax = 192, z = 192, edx = 16, y = 12
	movl	$LC0, (%esp)	;	_printf.arg[0] ← LC0	;	_printf.arg[0] = LC0, _printf.arg[1] = 192, eax = 192, z = 192, edx = 16, y = 12
	call	_printf			;	calls _printf			;	stdout = "Hello World 192", z = 192, y = 12

If you take a close look at the state of variables, you’ll understand what happens at every step of execution. Hope this helps. :slightly_smiling_face:

1 Like

We’ve had about three of these questions all of a sudden over the last few days - is some variant of it appearing in an exam/ test somewhere?

2 Likes

I guess some interviewers exaggerate their own intelligence by asking such questions. I’ve faced a question very similar to this:

write the output of printf("%d %d %d", i, i++, ++i)

in a test myself where we did not have access to gcc :slightly_frowning_face: . This confuses many students because it’s quite uncommon to come across such codes. :slightly_smiling_face:

1 Like
int z=(++y * ((y++) +5));

The expression gets evaluated left-to-right.
Initially, y=10;
So, z=(11*(11+5));
++y returned the incremented value, i.e. 11.
y++ returned the current value and then incremented, i.e. it returns 11, and then increments y to 12
Output
Hello World 176

Think of y++ as first-return then increment and,
of ++y as first-increment then return value
:smiley:

The answer: z won’t be having a well-defined value after the assignment statement, meaning the value it takes may vary across implementations (there is no standard for undefined behavior!)

Now, the how: here are the two essential concepts at play here: sequence points and side effects. I’ll try to explain them in terms as simple as I can. When a machine deals with an expression that might seem somewhat straightforward to us it needs to know where to start and how to keep going. Precedence and associativity rules provide an intuitive solution. So, where lies the uncertainty? It’s in the order of operations within an expression. Say for example we have an expression involving x+y somewhere and following the classic rules, say this sub-expression is to be evaluated next. What I meant by order is this: we don’t know which side, x or y is to be evaluated first. Now why we don’t know that is because + does not define a sequence point.

Formally, a sequence point defines any point in the program execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed; a side effect is a result of an operator, expression, statement, or function that persists even after the operator, expression, statement, or function has finished being evaluated.

A mouthful, I know. In simple words, everything that a section of code does other than what is expected off of it can be called a side effect (like a by-product of a chemical reaction, so maybe sometimes you may want to utilize the result of side effects). Say you want to assign the incremented value of x to y as:

                                    y=++x;

But in doing so, you also modified the value of x (a side effect). Probably you want it, probably you don’t but the semicolon guarantees that x will hold the modified value after this statement ( ; is a standard sequence point, everything to its left would have occurred before going through everything on its right or well after it). Similarly, the post-increment operator would not have assigned y with x’s modified value but after the semicolon it is guaranteed that x would have been modified.

Finally, in this statement in your code:

there are multiple modifications to y and since nothing before the semicolon is a standard sequence point, the value of the expression on the right of assignment is kind of ambiguous to the machines. Well, in the end, it’s them who run the codes, don’t they? :slightly_smiling_face:

For more:
Expressions
Sequence Point
Side Effects

2 Likes