2

I'm familiar with with nested scope rules in C, where same variable name inside the nested block shadows the outer variable with the same name. But for the following code snippet I'm unable to determine the explanation for the output.

#include <stdio.h>
int main()
{
    int x = 1, y = 2, z = 3;
    printf(" x = %d, y = %d, z = %d \n", x, y, z);
    {
        int x = 10;
        float y = 20;

        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    {
        int z = 100;
        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    return 0;
}

The output of the above snippet is:

x = 1, y = 2, z = 3

x = 10, y = 20.000000, z = 3

x = 1, y = 20.000000, z = 2

Will anyone please help me to understand how the value of yin the third printf statement producing a value of the variable that's out of scope.

First I thought it might be garbage value since printing integer with %f results in garbage value, so changing the value of y in the inner scope to some other value results in the same value as output,hence I'm confirmed that it's not a garbage value.

I've compiled the program using gcc version 6.3.1 20161221 (Red Hat 6.3.1-1) (GCC), as well as compiled the program using various online compiler.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Do you know why the value of `z` not get updated to 100 in the third scope? – Gaurav Pathak Jun 14 '17 at 10:11
  • Can't reproduce. –  Jun 14 '17 at 10:11
  • Given the answers bellow, I think it's safe to say you had a copy-paste error... – StoryTeller - Unslander Monica Jun 14 '17 at 10:14
  • 5
    You are trying to print an integer using %f. I am sure the compiler would have complained. But I am also sure that you must have disabled the warnings. – Ajay Brahmakshatriya Jun 14 '17 at 10:14
  • @GauravPathak No I didn't at the time of posting the question but now thanks to the answer given by Paul Ogilvie I got to know. – Shubhodeep Mitra Jun 14 '17 at 11:15
  • @AjayBrahmakshatriya It did give a warning about mismatch of specifier, I gave a thought that it should be undefined but since all the compilers were providing the same result, I needed some explanation apart from that fact that I'm using %f instead of %d for integer, well Paul Ogilvie gave a good explanation of what might happen in the background. – Shubhodeep Mitra Jun 14 '17 at 11:22

5 Answers5

10

You have a undefined behavior:

{
    int z = 100;
    printf(" x = %d, y = %f, z = %d \n", x, y, z); // here y is an int
}

If your flags in printf() don't match the good type, this is undefined bahavior.

You must use %d flag.

{
    int z = 100;
    printf(" x = %d, y = %d, z = %d \n", x, y, z); // here y is an int
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91
8

The problem is with mismatched format specifier and supplied argument. let's have a closer look.

In the first inner scope

{
    int x = 10;
    float y = 20;

    printf(" x = %d, y = %f, z = %d \n", x, y, z);
}

y is (re)-defined as float and using that variable as argument to %f conversion specifier is perfectly fine. However, once the scope ends, (the visble) y is an int, again. Hence in the second inner scope

 printf(" x = %d, y = %f, z = %d \n", x, y, z);
                  ^^^^^^

using %f is erroneous and causes undefined behavior. You must use %d for printing the value of y there.

Related, quoting C11, chapter §6.2.1/P4

Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier).

[...]

If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block.

[....]

If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

And, related to the UB, from chapter §7.21.6.1

[...] If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

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

First I thought it might be garbage value since printing integer with %f results in garbage value, so changing the value of y in the inner scope to some other value results in the same value as output,hence I'm confirmed that its not a garbage value.

This is a wrong assumption. It is "garbage" because you have undefined behavior here.

I'm citing myself with an explanation of undefined behavior:

Undefined behavior in C

C is a very low-level language and one consequence of that is the following:

Nothing will ever stop you from doing something completely wrong.

Many languages, especially those for some managed environment like Java or C# actually stop you when you do things that are not allowed, say, access an array element that does not exist. C doesn't. As long as your program is syntactically correct, the compiler won't complain. If you do something forbidden in your program, C just calls the behavior of your program undefined. This formally allows anything to happen when running the program. Often, the result will be a crash or just output of "garbage" values, as seen above. But if you're really unlucky, your program will seem to work just fine until it gets some slightly different input, and by that time, you will have a really hard time to spot where exactly your program is undefined. Therefore avoid undefined behavior by all means!.

On a side note, undefined behavior can also cause security holes. This has happened a lot in practice.

You're hitting one of the cases where the program seems to do something resembling correct execution, this is bad luck.

But it's undefined and as proof see what happens on my machine using your unmodified code:

$ ./scope
 x = 1, y = 2, z = 3
 x = 10, y = 20.000000, z = 3
 x = 1, y = 0.000000, z = 3

side note: although shadowing is well-defined, you should avoid it as well. It's just to risky to create buggy code with shadowing because, although the compiler hasn't a problem, you start to confuse your variables.

1

Try the way in the code below! ;)

The problem is as indicated in the other answers. You forget that in the scope the y variable is an int!!!

#include <stdio.h>

int main()
{
    int x = 1, y = 2, z = 3;
    printf(" x = %d, y = %d, z = %d \n", x, y, z);
    {
        int x = 10;
        float y = 20;

        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    {
        int z = 100;
        printf(" x = %d, y = %f, z = %d \n", x, (float)y, z);
    }

    return 0;
}
Sir Jo Black
  • 2,024
  • 2
  • 15
  • 22
1

The undefined behaviour, as identified in other answers, is explained on my Intel platform with VC as that the %f format specifier expects a double on the stack, which is larger than an int so when it retrieves the value, it retrieves more bytes than of an int and now assumes the next parameter at a diferent position on the stack, causing z to be printed wrong (i.e. it is not z that is printed).

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • Thanks for the insight now I understand the reason for the value of y , I totally failed to notice that the value of z is also wrong. – Shubhodeep Mitra Jun 14 '17 at 11:07