4

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.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261

2 Answers2

7

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.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Also, I think in case of `i=0` ,`if` statement wont work. – SKD Feb 12 '16 at 06:40
  • 2
    Won't it only be UB the first time through the loop? After the first loop, J is assigned. – Robert Harvey Feb 12 '16 at 06:40
  • 4
    @RobertHarvey Nopes, each `j` is unique. Please see §6.2.4/6 – Sourav Ghosh Feb 12 '16 at 06:42
  • Then the OP's assumption that the same J is used in all loop iterations must be invalid. I would expect a new J to be created each time, so I was a bit surprised by the OP's assertion, but I'm not a C expert. – Robert Harvey Feb 12 '16 at 06:43
  • @Sourav Ghosh Is it means if we have a declaration of `j` in N times loop, the compiler will allocate N x `j` size in the stack for this function? – Holsety Feb 12 '16 at 06:53
  • no its just that it comes into the loop block, then *exits* the loop block, before jumping back to the for() line that is *before* the loop block, and thus enters and exits the same block N times. just if you'd put a lable *before* the loop block, and a goto ThatLable *after* the loop block. all the variables in the block die after each exit of the block so there is no reason fro multiple storages of j – matt Feb 12 '16 at 06:57
  • 1
    @Holsety no, it re-uses the same space. Conceptually the variable is allocated when execution reaches `int j;` is reached, and deallocated when the following `}` is reached. The compiler will optimize this by just assigning a particular stack location to `j` – M.M Feb 12 '16 at 06:58
  • @Holsety no `N X j` will be there. Once every time loop ends, reaches the closing `}`, the associated `j` is vanished. :) – Sourav Ghosh Feb 12 '16 at 07:01
0

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.

matt
  • 4,042
  • 5
  • 32
  • 50
  • Just being nitpicky: I would not _promote_ usage of `goto` when it can clearly be avoided. – Sourav Ghosh Feb 12 '16 at 07:10
  • Oh god, I wasn't :P just using it to illustrate the fact that the loop does exit the loop block and thus destroy the block's local autos *each loop iteration* – matt Feb 12 '16 at 07:13