26

I faced this strange behavior when I was coding. So I ask it here.

What is the scope of a for loop when declaring variables?

This code compiles fine

for (int i = 0; i < 10; i++) { }

for (int i = 0; i < 10; i++) { }

This means both int i are not in same scope.

But this code does not compile.

for (int i = 0; i < 10; i++) { }
int i; // Conflicts with both first loop and second one.
for (int i = 0; i < 10; i++) { }

This means the int i in middle of loops has the same scope of first loop and the second loop.

But how can int i in two for loops have different scope, but the same scope with middle int i? Because currently I see them at the same level.

I know the second code does not compile. Why does the first code compile then if there is problem in scopes. Is this an exception inside the compiler?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • 1
    It's not in the same scope, but in a nested one, which is forbidden too. Take a look at this question for an answer to a similar problem: https://stackoverflow.com/questions/6156449/why-cant-a-duplicate-variable-name-be-declared-in-a-nested-local-scope – Dirk Sep 03 '15 at 09:30
  • 2
    Interestingly, `for(int i = ...) {} {int i; } for(int i = ...) {}` (note inner `{}`) does complie. – Dmitry Bychenko Sep 03 '15 at 09:30
  • 1
    @DmitryBychenko: This is because i exists only inside the block defined by the curly braces (between the for-loops). – Pedro Isaaco Sep 03 '15 at 09:36
  • 2
    It doesn't matter if the outer `i` is before or after the first `for`-loop. The compiler doesn't want it. Point. The compiler error prevents you from careless mistakes if you move the declaration from bottom to top. It's easy to avoid. – Tim Schmelter Sep 03 '15 at 09:43
  • Since no answers stated that: The assumption `This means the int i in middle of loops has the same scope of first loop and the second loop.` is false, and it's the main problem here. – bunyaCloven Sep 03 '15 at 15:24
  • How is this question not duplicate 7 years after Stack Overflow was launched? – Peter Mortensen Sep 03 '15 at 17:35

6 Answers6

27

The C# compiler does not check whether a variable was declared before or after another variable. All that matters is the scope. The i variable declared between loops surely conflicts with the second loop, because if you use i inside the loop, there is no way to distinguish which i you'd like to use. As for the first loop, an error is still shown, because the block where i is declared encapsulates also the first loop.

For example, the following will not compile, even though j is not visible outside inner braces, so there should not be any ambiguity regarding i:

{
    {
        int i = 1;
        int j = 1;
    }

    int i = 0; // compiler error: A local variable i cannot be declared in this scope (...)
    // j is not visible here
}

Edit regarding the comment:

Why is the following fine?

{
    for(int i = 1; i < 10; i++) {}
    for(int i = 1; i < 10; i++) {}
}

When you declare a for loop variable, it is visible only inside the loop block. That means that the scopes of both variables are disjoint, since there is no line of code where one block "overlaps" the other one.

Kapol
  • 6,383
  • 3
  • 21
  • 46
  • 1
    yes. sure it conflicts with second loop however this one does not compile too. `for (int i = 0; i < 10; i++) { }` `int i;`. but then how `for (int i = 0; i < 10; i++) { }` `for (int i = 0; i < 10; i++) { }` is possible? – M.kazem Akhgary Sep 03 '15 at 09:38
  • 2
    @M.kazemAkhgary as most answers explain, the scope of a variable *doesn't* start with its declaration, it's the entire block. A `for` loop defines a block – Panagiotis Kanavos Sep 03 '15 at 09:42
  • Thanks for the answer. so its like `{int i; } { int i; }` which is possible. – M.kazem Akhgary Sep 03 '15 at 09:47
  • @M.kazemAkhgary Yes, you can look at it this way. – Kapol Sep 03 '15 at 09:52
  • 5
    Wait, doesn't all this simply mean that local variables cannot shadow outer variables plus the fact that a declaration scope is the whole block and not just the point where it's written and forward? All this would be clearer simply saying that `for (int i; ...) int i;` is the same as `int i; for (int i; ..)`. – Bakuriu Sep 03 '15 at 17:56
8

The scope of a for loop, for(INIT; COND; INCR) { BLOCK } is identical in scoping to

{
    INIT;
    while (COND) {
        BLOCK;
        INCR;
     }
 }

Thus a for loop can be best thought of as two nested scopes. (Note: the above conversion from for to while does not properly capture the behavior of continue. However, this question is not focused on that)

The issue you run into with the int i outside of the for loop is something called "shadowing." In C++, if you declared a scoped variable with the same name as something in an outer scope, you "shadowed it," silently covering it up until the scope ended. When they developed C#, they felt this was too counterintuitive, and too error prone. In C# it is a syntax error to shadow a variable from an outer scope. By introducing int i to the outer scope, it is now illegal for the for loops to introduce it themselves.

Cort Ammon
  • 10,221
  • 31
  • 45
  • Great job mentioning the alternative scoping. I wanted to include this detail in my answer, but I didn't remember how exactly `for` can be translated. – Kapol Sep 03 '15 at 17:54
3

The variable declared in for loop has just scope inside for loop block, but when you declare a variable outside for loop, you cannot have same name variable inside the for loop, because it confuses compiler that which variable you mean in for loop body.

Like i will take your code as example:

int i =0;
for (int i = 0; i < 10; i++) 
{
  i = i+1; // now compiler is confused which i you mean here, so i complains on compile time that you have two with same name
}

So if you declare it between loops as you did, variable i has scope in both for loops so it is accessible in both for loops, so if you remove first loop it will still complain because of global scope of variable outside the loop:

for (int i = 0; i < 10; i++) 
{
  i = i+1; // now compiler is still confused which i you mean 
}
int i =0;
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • This is not the case, You got it wrong. OP said it conflicts with the declaration of `i` in first loop. You should try to compile OP's code. – Shaharyar Sep 03 '15 at 09:35
  • 1
    `for (int i = 0; i < 10; i++) { } int i = 20;` why compilor confused?? – Praveen Sep 03 '15 at 09:36
  • because we have two variables, 2nd one is global so its scope is also inside for loop – Ehsan Sajjad Sep 03 '15 at 09:37
  • But why this global variable conflicts with the inner variable of first loop? Even this global `i` was declared after the scope of first loop. – Shaharyar Sep 03 '15 at 09:38
  • 1
    it is global whether it is written before or after loop, point is outside loop variable has global scope for both loops – Ehsan Sajjad Sep 03 '15 at 09:39
  • 2
    @Shaharyar it doesn't matter where the 'I' is declared as long as it is in the same scope. It could be before or after, C# doesn't compile from top to bottom. – Ryan Searle Sep 03 '15 at 09:40
  • @RyanSearle Yes! You're correct about it but then how it works if it doesn't go top to bottom? – Shaharyar Sep 03 '15 at 09:50
  • @Shaharyar I think of it more of compiling outside in, what i mean by that is it compiles the outer scopes first before it compiles the inner scopes. I would recommend doing further researching this as my knowledge is limited. – Ryan Searle Sep 03 '15 at 09:55
  • 1
    @RyanSearle Thank you for adding it to my knowledge :) – Shaharyar Sep 03 '15 at 10:03
  • @Shaharyar code is compiled to IL and on compile it checks code for errors, so as it finds two variable of same name within a scope it complains, as RyanSearle said order does not matter here – Ehsan Sajjad Sep 03 '15 at 10:08
  • @EhsanSajjad I know how it compiles the code, but I didn't know that order doesn't matter. – Shaharyar Sep 03 '15 at 10:09
  • @RubberDuck what i mean was it has scope now in both loops as it is global for both loops – Ehsan Sajjad Sep 03 '15 at 12:16
  • Global is a very poor choice of words @EhsanSajjad. Global scoped variables are an actual thing, and these are not Globally scoped. These are privately scoped to the method, which *contains* the loop's scope. – RubberDuck Sep 03 '15 at 12:36
  • @RubberDuck i admit i used wrong word for it, thanks for the feedback – Ehsan Sajjad Sep 03 '15 at 12:48
2

There is nothing strange going on. In the second case you've defined an outer i and then try to redefine it in each loop.

Variables declared in a for statement are local to the loop, but you've already defined another variable with the same name in the outer scope.

I think you've already asked another scoping question assuming that the scope of a variable starts from the point of declaration ?

A variable's scope is the block in which it is defined, it isn't affected by its placement. While the compiler will refuse to use the variable before the declaration, it is still in scope

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 1
    Actually OP's code is conflicting with the first for loop, you didn't explain anything about it. – Shaharyar Sep 03 '15 at 09:37
  • @Shaharyar actually, I did - I wrote about inner and outer loops, not first or second. The outer conflicts with *both* loops. Comment either of them and you'll see that you still have a conflict. That's why I *didn't* specify one or the other loop – Panagiotis Kanavos Sep 03 '15 at 09:40
  • In a way, a variable isn't "in scope" until after it's been declared. You can't use `i` before you declare it. This behavior was likely the simplest implementation for the compiler team. Compilers are *hard* though, so I don't blame them. – RubberDuck Sep 03 '15 at 10:55
2

I don't think it matters where you put int i;.

The compiler first scans the field, after which it starts scanning for expressions. It doesn't compile because the i is already recognized as a field.

Yaw
  • 70
  • 8
1

int i; You declare outside the loop is available for the current function. Either declare only outer int i, and remove int i from both loops, or just remove this outer variable.

shA.t
  • 16,580
  • 5
  • 54
  • 111
M_Idrees
  • 2,080
  • 2
  • 23
  • 53