Variable created inside loop changes value during iterations in C

2.4k views Asked by At

I have code similar to the following in our product. According to me, the output is '0 1 2 3'. But the output of the similar code is '1 1 1 1'.

for(i = 0 ;i < 5;i++){
    int j;
    if(i)
        printf("%d ",j);
    j = i;
}

My understanding is that the j is allocated on the stack only once during the entire period of 'for' loop and the same value is used during iterations. Also, if I move the declaration of j outside for loop, I'm getting the expected result. What am I missing here?

PS - When I run the same code on my personal machine, I am getting the expected output. But on production it is different.

2

There are 2 answers

8
Sourav Ghosh On BEST ANSWER

First, to clear things about the storage duration of an automatic local variable, let me quote the C11 standard, chapter §6.2.4, (emphasis mine)

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, [...]

and,

For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate.

So, in your code, each iteration gets a new instance of j. Nothing is retained.

In your code,

    int j;   //not initialized
    if(i)
        printf("%d ",j);  //this one here

you're trying to use an unitialized automatic local variable j, which has indeterminate value. It invokes undefined behavior.

As per C11, chapter §6.7.9

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate

and related, for UB, annex §J.2

The value of an object with automatic storage duration is used while it is indeterminate.

Once your code hits UB, the output cannot be justified, anyway.

OTOH, when you declare j outside the loop, it has function scope. Then, unlike above case, there will be only one instance of j for all iterations of the loop.

As per the execution flow, first time, i being 0, if will evaluate to false, printf() will be skipped and j will get initialized. Then, in next iteration, when you hit the printf(), j is initialized and it's all well thereafter.

2
matt On

For some clarity, I think the for loop would be converted under the hood to something like:

i = 0;
LoopStart:
if(!(i<5)){ goto LoopEnd;}

{
    int j;
    if(i)
        printf("%d ",j);
    j = i;
}

i++;
goto LoopStart;
LoopEnd:

Actual implementations would differ, but this serves to highlight this point: The block is entered and exited for each iteration of the loop, meaning each auto in the block is created and destroyed 5 times in this example. as others mentioned, this means you are using an uninitialized j each time in your printf.

As for why the code might work on some platform / compilers. its probably because j is allocated the same stack address each time, and the compiler doesn't clear the stack when it creates or destroys j, so it just so happens the last value assigned to old, dead j, is accessible through the new, uninitialized one.