17

I don't understand how the following code works:

#include "stdio.h"

int main(void) {
  int i = 3;
  while(i--) {
    static int i = 100;
    i--,
    printf("%d\n", i);
  }
  return 0;
}

The code compiled with either Clang or GCC prints the following output:

99
98
97

Can someone explain to me what happens here? It looks like two operations are achieved in a single instruction and more than once. Is it undefined behavior? I observe the same behavior in C++.

200_success
  • 7,286
  • 1
  • 43
  • 74
Xatyrian
  • 1,364
  • 8
  • 26
  • 1
    Possible duplicate https://stackoverflow.com/questions/22120362/shadowing-of-static-global-and-local-identifiers – StoryTeller - Unslander Monica Jan 31 '18 at 10:18
  • 3
    If you turn your compiler Flags ON you’ll see that the compiler warns you about the declaration which shadows another one. At least GCC does that. – Michi Jan 31 '18 at 10:31
  • 2
    "It looks like two operations are achieved in a single instruction and more than once." Could you please specify what you mean and which output you expected? – Gerhardh Jan 31 '18 at 10:31
  • Maybe off topic, but why did they design compilers to allow shadowing? Only use case I can think of is maybe you include a c library that uses a common name? – sudo rm -rf slash Jan 31 '18 at 17:44
  • "two operations are achieved in a single instruction" - where exactly do you see something like that? – AnT stands with Russia Feb 01 '18 at 05:44

3 Answers3

24

This is not undefined behavior.

#include "stdio.h"

int main(void) {
  int i = 3; //first i
  while(i--) {
    static int i = 100; //second i
    i--,
    printf("%d\n", i);
  }
  return 0;
}

In while loop body most local i (second i) is preferred. While checking condition in while loop it doesn't know what is there in body. So it has no problem choosing first i.

haccks
  • 104,019
  • 25
  • 176
  • 264
Pranit Kothari
  • 9,721
  • 10
  • 61
  • 137
  • 8
    Just an information.. static int i = 100; is executed only once.. If it was not static result would have been 99, 99 , 99 – Sreeragh A R Jan 31 '18 at 10:33
  • @SreeraghAR True. – Pranit Kothari Jan 31 '18 at 10:34
  • 2
    @Sreeragh A R: That is misleading. In both C and C++ `i` is initialized *statically* (aka "at compile time"). It is never really executed even once. And C language does not even have a concept of "executing" initialization of static objects. Even though the initializer appears to be written inside the function, the actual initialization takes place outside its bounds. – AnT stands with Russia Feb 01 '18 at 05:45
  • @AnT You may be right..I just wanted to convey that the result would have been different if it was not static.. – Sreeragh A R Feb 01 '18 at 05:59
11

Wikipedia says very prominent thing on this:

In computer programming, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope. At the level of identifiers (names, rather than variables), this is known as name masking. This outer variable is said to be shadowed by the inner variable, while the inner identifier is said to mask the outer identifier.

Now here inside the block it finds the static variable and works on it but the while condition decrements the i which is the one declared outside the block. The scope being different - it is no problem to use the correct value of i. This is legal C code but not necessarily a good way of writing things.

In fact doing this gcc -Wshadow progname.c gives

progname.c: In function 'main':
progname.c:7:20: warning: declaration of 'i' shadows a previous local [-Wshadow]
         static int i=2;
                    ^
progname.c:5:9: warning: shadowed declaration is here [-Wshadow]
     int i=2;
         ^

From standard §6.2.1p4

... 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.

user2736738
  • 30,591
  • 5
  • 42
  • 56
2

Its possible to declare a same named variable inside a nested scope. The compiler sees them as different variables. It is very confusing but the variable you are accessing each time is the one declared in the inner most scope. Outside the while it is the int i = 3; and inside it is the static int i = 100;

#include "stdio.h"

int main(void) {
  int i = 3; // outer i
  while(i--) { // outer i
    static int i = 100; // inner i
    i--, // inner i
    printf("%d\n", i); // inner i
  }
  return 0;
}

If this was a function other than main, then the 2nd call to it would produce

96
95
94

and so on...

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • If you turn your compiler Flags ON you’ll see that the compiler warns you about the declaration which shadows another one. At least GCC does that. – Michi Jan 31 '18 at 10:31
  • Sure. It warns you that it refers to the inner most variable, while the programmer might not. The warning explains the behavior – CIsForCookies Jan 31 '18 at 10:33