Question

#include<stdio.h>

int main() 
{
    int i  = 10;

    printf("0 i %d %p\n",i,&i);
    if (i == 10)
        goto f;

    {
        int i = 20;
        printf("1 i %d\n",i);
    }
    {
        int i = 30;
        f:
        printf("2 i %d %p\n",i,&i); //statement X
    }

    return 0;
}

Output:

[test]$ ./a.out 

0 i 10 0xbfbeaea8

2 i 134513744 0xbfbeaea4

I have difficulty in understanding how statement X works?? As you see the output it is junk. It should rather say i not declared??

Was it helpful?

Solution

That's because goto skips the shadowing variable i's initialization.

This is one of the minor nuances of the differences between C and C++. In strict C++ go to crossing variable initialization is an error, while in C it's not. GCC also confirms this, when you compile with -std=c11 it allows while with std=c++11 it complains: jump to label 'f' crosses initialization of 'int i'.

From C99:

A goto statement shall not jump from outside the scope of an identifier having a variably modified type to inside the scope of that identifier.

VLAs are of variably modified type. Jumps inside a scope not containing VM types are allowed.

From C++11 (emphasis mine):

A program that jumps from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer.

OTHER TIPS

From the output, it is clear that the address of 'i's are unique, since they are declared in different scopes.

0 i 10        0xbfbeaea8

2 i 134513744 0xbfbeaea4

how statement X works?? As you see the output it is junk. It should rather say I not declared??

i is also declared in the local scope of statement x but the initialization of i to 30 is skipped because of goto statement. Therefore the local variable i contains a garbage value.

In the first printf statement, you accessed the i in address 0xbfbeaea8 which was declared and initialized in the statement int i = 10;

Once you hit the goto f; statement, you are in the scope of the 2nd i, which is declared at this point and resides in address 0xbfbeaea4 but which is not initialized as you skipped the initialization statement.

That's why you were getting rubbish.

When control reaches the third block, i is declared for the compiler, hence i represents some memory address therefore compiler tries to read it again. But since i has now become out-of-scope, you cannot be sure that it will contain the same value what it originally had.

My suggestion to understand somewhat complex code is to strip out, one by one, all "unnecessary" code and leave the bare problem. How do you know what's unnecessary? Initially, when you're not fluent with the language, you'll be removing parts of the code at random, but very quickly you'll learn what's necessary and what is not.

Give it a try: my hint is to start removing or commenting out the "goto" statement. Recompile and, if there are no errors, see what changed when you run the program again.

Another suggestion would be: try to recreate the problem "from scratch": imagine you are working on a top-secret project and you cannot show any single line of code to anyone, let alone post on Stack Overflow. Now, try to replicate the problem by rewriting equivalent source code, that would show the same behaviour.

As they say, "asking the right question is often solving half the problem".

The i you print in this printf("2 i %d %p\n",i,&i); statement, is not the i which value was 10 in if statement, and as you skip this int i = 30; statement with goto you print garbage. This int i = 30; is actual definition of the i that would be printed, i.e. where compiler allocates room and value of i.

The problem is that your goto is skipping the assignment to the second i, which shadows (conceals) the first i whose value you've set, so you're printing out an uninitialized variable.

You'll get a similar wrong answer from this:

#include<stdio.h>

int main() 
{
    int i  = 10;                                  /* First "i" */

    printf("0 i %d %p\n",i,&i);

    {                                             /* New block scope */
        int i;                                    /* Second "i" shadows first "i" */
        printf("2 i %d %p\n",i,&i);
    }

    return 0;
}

Three lessons: don't shadow variables; don't create blocks ({ ... }) for no reason; and turn on compiler warnings.

Just to clarify: variable scope is a compile-time concept based on where variables are declared, not something that is subject to what happens at runtime. The declaration of i#2 conceals i#1 inside the block that i#2 is declared in. It doesn't matter if the runtime control path jumps into the middle of the block — i#2 is the i that will be used and i#1 is hidden (shadowed). Runtime control flow doesn't carry scope around in a satchel.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top