21
int main() {
    int j = 0;
    int i = 0;
    for (j = 0; j < 5; j++) {
        printf("Iteration %d :  %d ", j + 1, i);
        int i;
        printf("%d", i);
        i = 5;
        printf("\n");
    }
}

The above code generates the following output:

Iteration 1 :  0 0
Iteration 2 :  0 5
Iteration 3 :  0 5
Iteration 4 :  0 5
Iteration 5 :  0 5

I'm not able to understand why the second printf value in iterations 2,3,4,5 is 5.

My understanding of why the first value is 0 in each iteration is that the scope of i in the for loop is local and it is destroyed as soon as we go into a new iteration as i was declared in the for loop.

But I'm not able to figure out why this value becomes 5 at the second printf.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Milan
  • 403
  • 2
  • 8

5 Answers5

54

The behaviour of your program is undefined.

The inner scope i is not initialised at the point it's read.

(What might be happening is that the reintroduced i on subsequent iterations occupies the same memory as the previous incarnation of the inner i, and the uninitialised memory on the first iteration corresponds to 0. But don't rely on that. On other occasions, the compiler might eat your cat.)

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 4
    No, `int` can contain a trap representation. If it was `unsigned char` then you would be correct. – Bathsheba Feb 23 '17 at 15:29
  • 3
    @Bathsheba it wouldn't be correct then even. It doesn't have its address taken, so it has undefined behaviour. And if it did have its address taken, it would have indeterminate value (even if unsigned char) and using that in a library function would have undefined behaviour. – Antti Haapala -- Слава Україні Feb 23 '17 at 15:33
  • 3
    It is always UB as per [this](http://stackoverflow.com/a/40674888/584518). – Lundin Feb 23 '17 at 15:36
  • In most platforms (wether you count deployed CPUs or number of individual architectures), int can't contain a trap representation. Still undefined behavior, of course. – hyde Feb 23 '17 at 15:36
  • It's always UB. If the type was `unsigned char`, then there wouldn't be a trap representation. – Jonathan Leffler Feb 23 '17 at 15:37
  • 5
    Beating the dead horse, I already linked an answer which states why it is always UB. Trap representation or no trap representation doesn't matter. As Antti says, it is because it's an automatic variable that doesn't have its address taken. C11 6.3.2.1. – Lundin Feb 23 '17 at 15:38
  • 9
    "On other occasions, the compiler might eat your cat." Oh come on! Not that shit again, compiler! You already ate 50 cats of mine just in the last hour. No wonder you have gained so much installation size over the years with all those cats you are eating. – Kaiserludi Feb 23 '17 at 18:04
  • @Lundin: The post you linked states that if the object has its address taken, and its type has no trap representations, then the variable will contain an Unspecified value. Such a construct allows constructs like `struct string15 {char[16] st;} mystring;` and then within a function `struct string15 x; strcpy(x.st,"Hello"); mystring = x;`, to avoid wasting time writing portions of `x.st` which are irrelevant to program operation. The write-up for a defect report suggests that if the above were followed by `int a=mystring.st[15], b=mystring.st[15], c=a-b;`... – supercat Feb 23 '17 at 18:56
  • ...there would be no guarantee that `a==b`, or that `c==0`, but any implementation that couldn't guarantee such things should be considered unsuitable for many purposes, including anything that would have any security implications (while security-related code should of course initialize buffers properly, it also needs the ability to take a snapshot of an object, validate it, and know that the snapshot won't spontaneously change after validation). It would be reasonable for an implementation to require use of directives to convert Indeterminate values to Unspecified values... – supercat Feb 23 '17 at 19:04
  • ...if such directives existed, but of course the Standard doesn't define any, and any code that tried to use compiler-specific directives for such purpose would likely be unusable on implementations which don't define any because ordinary assignment does the same thing. – supercat Feb 23 '17 at 19:09
  • In the old days of comp.lang.c on Usenet, the behaviour of such code typically resulted in daemons flying out of your nose. – Laconic Droid Feb 24 '17 at 00:02
  • @supercat I believe there is already a defect report regarding whether several reads on the same variable with unspecified value could yield different values or not. http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_451.htm – Lundin Feb 24 '17 at 07:29
  • "Not that shit again, compiler!" Oh come on! Not that shit again, @Kaiserludi! You already fed the compiler 50 cats just in the last hour. No wonder you have gone through so many cats over the years with all those UBs you are coding. – philipxy Feb 24 '17 at 11:46
  • 1
    @Lundin: I've read the report. There are some application fields where the execution model described thereby might be useful, but many application fields require a means of converting a possibly-Indeterminate Value into one that is at worst Unspecified, and neither the Standard nor common practice suggests any means by which that might be accomplished on a compiler which doesn't regard assignments as performing such a transformation. While security-conscious code should scrub any buffers before use, high-performance code which doesn't need to be security conscious should not need to... – supercat Feb 24 '17 at 16:55
  • 1
    ...pre-scrub storage in cases where any pre-existing bit pattern would work equally well. The aggressive optimizations motivating the DR would be blocked by code which pre-scrubbed buffers, so such optimizations can only have value if they allow code to have useful semantics without pre-scrubbing. I wish Standards Committee would remind compiler writers that allowing a behavior is merely an invitation for compiler writers to *excercise judgment* as to whether such behavior would serve their implementations' purposes; it are not intended as a *substitute* for such judgment. – supercat Feb 24 '17 at 17:03
  • 1
    @Lundin: Unfortunately, there's an increasing rift between dialects of C targeting different purposes. There aren't any directives to convert Indeterminate values into Unspecified ones because implementations targeting fields where such semantics are sometimes needed provide them by default, and applications in fields where they're not needed, don't need them. If such directives were defined, code which relies upon such semantics without using the directives could be deprecated, allowing the dialects to be unified. I've seen no real moves in that direction, though. – supercat Feb 24 '17 at 17:08
10

The second printf in your program is printing garbage value from uninitialized local variable i. In general case the behavior is undefined.

By accident, storage location that represents your i (memory cell or CPU register) is the same on each iteration of the cycle. By another accident, the body of your cycle is executed as a single compound statement for each iteration (as opposed to unrolling the cycle and executing all iterations at the same time in interleaved fashion). By yet another accident, the storage location of i keeps its old value from the previous iteration. So, the garbage that you are printing matches the last stored value from that location on the previous iteration of the cycle.

That's why you see 5 in local i on each iteration besides the first. On the first iteration that garbage value happened to be 0.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
6

I think this is what is happening:

Breakpoint 1, main () at abc.c:4
4       int j = 0;
(gdb) s
5       int i = 0;
(gdb)
6       for(j=0;j<5;j++){
(gdb) p &i
$23 = (int *) 0x7fff5fbffc04           //(1) (addr = 0x7fff5fbffc04) i = 0
(gdb) p i
$24 = 0                                              // (2) i == 0
(gdb) s
7           printf("Iteration %d :  %d ",j+1,i);
(gdb) p &i
$25 = (int *) 0x7fff5fbffc00            //(3) here compiler finds there is contention in variable 'i' and assigns the inner one which is in present scope. Now, when subroutines are called, the stack frame is populated with (arguments, return address and local variables) and this is when the inner 'i' was also got allocated in this inner loop block but not initialized yet and this is the reason i get garbage value in the first integration output.

(gdb) p i
$26 = 1606417440                          // (4)  Here i == 1606417440 (Garbage)
(gdb) s
9           printf("%d",i);
(gdb) s
10          i = 5;
(gdb) p &i
$27 = (int *) 0x7fff5fbffc00
(gdb) p i
$28 = 1606417440
(gdb) s
11          printf("\n");
(gdb) p &i
$29 = (int *) 0x7fff5fbffc00
(gdb) p i                                                //(5) after executing previous statement, now i == 5
$30 = 5
(gdb) 
Milind Deore
  • 2,887
  • 5
  • 25
  • 40
1

C compilers have some latitude in how they implement things and it would not be at all unusual for the local "i" to not actually be recreated each pass of the loop. It is likely actually only being created once, and re-used on each pass. In this case, your first print is outside the scope of the local variable so uses the i from outside the loop. The second print uses the local i as it is in scope, and after the first iteration has been set to 5. On the first iteration, the correct value is undefined, not 0, but the behavior of undefined is implementation specific, in your implementation it appears to automatically be initialized to 0.

dlb
  • 357
  • 3
  • 13
0

If you declare a local variable and don't give it a value before using it, if you get is undefined behavior.

C standard say,

Variables with automatic storage duration are initialized each time their declaration-statement is executed. Variables with automatic storage duration declared in the block are destroyed on exit from the block.

msc
  • 33,420
  • 29
  • 119
  • 214
  • 4
    I don't see how this paragraph is relevant.. It really doesn't say anything about UB. – Eugene Sh. Feb 23 '17 at 16:03
  • This answer is incorrect and the cited part is irrelevant. [See this](http://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior-in-c/40674888#40674888). – Lundin Feb 24 '17 at 07:31