Why doesn't the C compiler give a redeclaration error inside a for loop body?

In the C language, if we write this:

for(int i = 0; i < 7; i++)
{
    // for loop Body
}

The scope of variable i is inside the for loop body. It is OK.

But, if I write this:

for(int i = 0; i < 7; i++)
{
    long int i = 1; // Redeclaration of i
}

Here, the variable i is declared again inside the loop body, yet it successfully compiles and runs in C.

But, in C++, the compiler gives a "redeclaration of 'long int i'" error.

So, why doesn't the C compiler give a redeclaration error? Is it a compiler bug?

3 answers

  • answered 2018-07-11 05:57 Sourav Ghosh

    First of all, there is no re-declaration inside the loop. Inside the loop body. i is defined every time the loop is entered, and the scope is only till the loop body.

    Once the loop finishes the iteration, the variable does not exist anymore. (First quote)

    That being said, what you are seeing is a result of shadowing. (Second quote)

    Quoting C11,

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

    and,

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

  • answered 2018-07-11 06:05 StoryTeller

    C++ and C make a distinction here. According to C11 (n1570) §6.8.5 ¶5, emphasis mine:

    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.

    Which translates to this for a for loop:

    {
        declaration 
        while ( expression) {
            statement
            expression ;
        }
    }
    

    Anything you put in the statement part can hide whatever is introduced in the declaration. Now, C++(17, n4659) explicitly says something similar at [stmt.for]/1. But it also goes on to add:

    except that names declared in the init-statement are in the same declarative region as those declared in the condition,

    So here the second i is indeed an attempt at a re-declaration. The above may sound confusing (names declared in the condidion!?), but the "condition" here is defined like this ([stmt.stmt]/1):

    condition:
        expression
        attribute-specifier-seq decl-specifier-seq declarator brace-or-equal-initializer
    

    It's that which allows while loops like this (C++ only):

    while (T t = x) statement 
    

    And you may not re-declare t inside the statement of the while loop.


    Alternatively, the entire conclusion I reach is summarized in [stmt.iter]/3 (thank you @T.C.):

    If a name introduced in an init-statement or for-range-declaration is redeclared in the outermost block of the substatement, the program is ill-formed.

  • answered 2018-07-11 06:17 Sailesh010

    It's not redeclaration. (Not literally, some may still call it though)

    See this carefully...

    for(int i = 0; i < 7; i++)
    {
        printf("i = %d\n", i);
        int i = 5;
        printf("new i = %d\n", i);
    }
    

    Output of the above code:-

    i = 0
    new i = 5
    i = 1
    new i = 5
    i = 2
    new i = 5
    i = 3
    new i = 5
    i = 4
    new i = 5
    i = 5
    new i = 5
    i = 6
    new i = 5
    

    Clearly, there are two different i's

    The newer i has a more local scope.

    Is it a bug?

    No

    What is the purpose?

    If it wasn't allowed, it might be very hard to maintain large projects, as you would constantly run into naming collisions.

    Generally though, it is considered very poor practice to give the same name to different variables in different scopes, you should avoid doing that whenever possible.

    Why is there no warning message?

    Use gcc file_name.c -Wshadow to compile.


    EDIT: You can also locally lock the originally declared variables by redeclaring them in for loops.