6

I have compiled following program using gcc prog.c -Wall -Wextra -std=gnu11 -pedantic command on GCC compiler. I wondered, it is working fine without any warnings or errors.

#include <stdio.h>

int main(void)
{
    for (int i = 0; i == 0;  i++) 
    {        
        printf("%d\n", i);
        long int i = 1; // Why doesn't redeclaration error?
        printf("%ld\n", i);
    }
}

Why compiler doesn't generate redeclaration variable i error?

msc
  • 33,420
  • 29
  • 119
  • 214
  • Are you also not getting a warning that `int main` is not returning a value? – MFisherKDX Nov 30 '17 at 06:33
  • @MFisherKDX Cppreference says: "The body of the main function does not need to contain the return statement: if control reaches the end of main without encountering a return statement, the effect is that of executing return 0;" – msc Nov 30 '17 at 06:36
  • 2
    Per 5.1.2.2.3 of n1570 (C11 draft): "reaching the } that terminates the main function returns a value of 0" (so long as the return type is compatible with `int`.) – lockcmpxchg8b Nov 30 '17 at 07:07
  • 1
    In C99 and later, for (misguided?) conformity with C++98, you're allowed to omit the `return 0;` at the end of `main()` (but only of `main()` — no other function gets the special treatment) and it is equivalent to `return 0;` at the end. Personally, I don't think it was a good decision and I have a policy of not taking advantage of the permission. I can't stop others from doing it, though — I can only recommend that they don't. – Jonathan Leffler Nov 30 '17 at 07:22
  • 3
    Possible duplicate of [Scope hiding in C](https://stackoverflow.com/questions/8928521/scope-hiding-in-c) – OmG Nov 30 '17 at 07:35
  • 1
    @OmG, no not at all a duplicate of that one. – Jens Gustedt Nov 30 '17 at 08:10
  • @OmG No, it's not duplicate. – msc Nov 30 '17 at 10:46

5 Answers5

6

In C language, the scope of statement is nested within the scope of for loop init-statement.

According to Cppreference :

While in C++, the scope of the init-statement and the scope of statement are one and the same, in C the scope of statement is nested within the scope of init-statement.

According to stmt:

The for statement

for ( for-init-statement conditionopt ; expressionopt ) statement

is equivalent to

{
    for-init-statement
    while ( condition ) {
            statement
            expression ;
      }
} 

except that names declared in the for-init-statement are in the same declarative-region as those declared in the condition, and except that a continue in statement (not enclosed in another iteration statement) will execute expression before re-evaluating condition.

msc
  • 33,420
  • 29
  • 119
  • 214
  • 1
    you should stop referring to c**pp**reference when answering your language-lawyer C questions. It is known to contain errors even for C++. – Antti Haapala -- Слава Україні Nov 30 '17 at 07:06
  • 1
    @AnttiHaapala, agreed that this is not always the best choice as a source. But here it is right and makes an important point, namely that C and C++ have different rules for that. There has even been a defect report on this for C (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2148.htm#dr_466), and it turned out that this difference was unintentional, but that it was too late to change that. – Jens Gustedt Nov 30 '17 at 08:15
  • @JensGustedt the actual standards are much better references. And that DR – Antti Haapala -- Слава Україні Nov 30 '17 at 08:24
  • 1
    @AnttiHaapala cppreference also has a C reference section, the corresponding page is [this](http://en.cppreference.com/w/c/language/for). (But I agree the standard is more authoritative of course). – alain Nov 30 '17 at 11:36
6

From standard §6.8.5.5 (N1570)

An iteration statement is a block whose scope is a strict subset of the scope of its enclosing block. The loop body is also a block whose scope is a strict subset of the scope of the iteration statement.

Emphasis added

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
user2736738
  • 30,591
  • 5
  • 42
  • 56
2

You have to set -Wshadow to get warnings on shadowed variables. Variable shadowing is allowed in C.

But this is an edge case. A var declared in the head of a for construction is not outside the brackets, because it has no scope after the construction.

This is not equivalent:

int i;
for( i = 0; …)
{ … }
// is is still in scope but wouldn't if declared in the head of for

But, it is not inside the brackets, too.

for( i = 0; …)
{ 
  int i; // this would be strange, because i is used before it is declared.
  … 
}

The best approximative replacement of the code is this:

{
  int i;
  for( i = 0; …)
  {
  … 
  }
}  // i loses scope

So it is no redeclaration, but a shadowing declaration inside the loop's body.

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • I don't think that this is what the question is about, but why the `for` declaration and the one inside the `{}` aren't in the same declaration scope. – Jens Gustedt Nov 30 '17 at 08:13
  • An identifier declare in tne `for` is outside the brackets used in the body. Its scope includes the expressions in the `for`. The scope of an identifier inside the brackets does not. – Eric Postpischil Nov 30 '17 at 12:46
  • @EricPostpischil Indeed. Why do you comment so? – Amin Negm-Awad Nov 30 '17 at 14:31
  • @AminNegm-Awad: Your answer says “A var declared in the head of a `for` construction is not outside the brackets, because it has no scope after the construction.” That is false. An identifier declared in the head of a `for` has scope outside the brackets. The part inside the `for` header is outside the brackets. – Eric Postpischil Nov 30 '17 at 14:52
  • @EricPostpischil Reading the whole post, it should be quite clear to everybody (at last nearly everybody), that this means "simply outside the brackets as a declaration in front of for". – Amin Negm-Awad Nov 30 '17 at 15:06
  • The sentence is false. The answer could be written better. – Eric Postpischil Nov 30 '17 at 15:25
  • @EricPostpischil No, picking up a single sentence from a text is wrong. – Amin Negm-Awad Nov 30 '17 at 15:30
  • A purpose of comments is point out things in the answer that could be improved. – Eric Postpischil Dec 01 '17 at 00:45
0

Why compiler doesn't generate redeclaration variable i error?

From C Standards#6.2.1p4 Scopes of identifiers

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 outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. 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 the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. 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.

From C standards#6.8.5p5 Iteration statements

An iteration statement is a block whose scope is a strict subset of the scope of its enclosing block. The loop body is also a block whose scope is a strict subset of the scope of the iteration statement.

So, in this code:

for (int i = 0; i == 0;  i++)
{
    printf("%d\n", i);
    long int i = 1; // Why doesn't redeclaration error?
    printf("%ld\n", i);
}

the scope of identifier with name i is overlapping and in this name space, the i declared in for (int i = 0; i == 0; i++) has outer scope and the one declared within loop body long int i = 1; has inner scope.

Within the loop body, after this statement:

long int i = 1;

the i declared in the outer scope is not visible and printf() printing the value of i visible in the inner scope which is 1.

This behavior is also known as Variable Shadowing which occurs when a variable declared within a certain scope has the same name as a variable declared in an outer scope.

C language allows variable shadowing and that's why the compiler does not throw any error for this. However, in gcc compiler, if you use -Wshadow option you will get a warning message - declaration shadows a local variable.

H.S.
  • 11,654
  • 2
  • 15
  • 32
0

For further verification i checked this code in visual studio 2008 for the prog.c file. I found that the compiler does give error in the line for (int i = 0; i == 0; i++) . The compiler expects declaration of i to be in the beginning of the program itself. This behavior is correct for a C file. If the declaration is moved to the beginning of the program then there are no errors as expected. All scope related issues are solved.

If i try this code as prog.cpp file, then the compiler does give an error for redeclaration. This is also an expected behavior.

So i conclude this has to do with the gcc compiler, does any flag/parameters that is used for compiling/building the exe results in this behavior for the gcc compiler.

Can rsp post the make file details for further verification?