'for'循环中声明变量的作用域

26

在编码过程中,我遇到了这个奇怪的行为。所以我在这里提问。

当声明变量时,for循环的作用域是什么?

这段代码可以顺利编译。

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

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

这意味着int i不在同一作用域中。

但是这段代码无法编译。

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

这意味着循环中间的 int i 与第一个循环和第二个循环有相同的作用域。

但是,为什么两个 for 循环中的 int i 可以具有不同的作用域,而与中间的 int i 具有相同的作用域呢?因为我当前看到它们处于相同的级别。

我知道第二段代码无法编译。如果作用域存在问题,为什么第一段代码能够编译?这是编译器内部的一个特例吗?


1
它不在同一作用域中,而是在一个嵌套的作用域中,这也是被禁止的。请查看此问题以获取类似问题的答案:https://dev59.com/vW025IYBdhLWcg3wNC51 - Dirk
2
有趣的是,for(int i = ...) {} {int i; } for(int i = ...) {}(注意内部的{})确实可以编译。 - Dmitry Bychenko
1
@DmitryBychenko:这是因为i仅存在于由花括号(在for循环之间)定义的块内。 - Pedro Isaaco
2
无论外部的 i 在第一个 for 循环之前还是之后都没有关系。编译器不允许这样做。编译器错误可以防止您在将声明从底部移动到顶部时犯粗心错误。这很容易避免。 - Tim Schmelter
由于没有回答指出:假设“这意味着循环中的int i具有第一个循环和第二个循环相同的作用域。”是错误的,这是主要问题。 - bunyaCloven
在 Stack Overflow 推出 7 年后,为什么这个问题不是重复的? - Peter Mortensen
6个回答

27

C#编译器不检查变量是在另一个变量之前还是之后声明。重要的只是范围。在循环之间声明的 i 变量肯定会与第二个循环产生冲突,因为如果你在循环中使用 i ,就没有办法区分你想使用哪个 i 。至于第一个循环,仍然会显示错误,因为声明 i 的块也封装了第一个循环。

例如,即使j在内部括号外不可见,以下代码也无法编译,因此不应该有任何关于 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
}
< p > < em > 关于评论的编辑:

< p>为什么以下内容是可以接受的?

{
    for(int i = 1; i < 10; i++) {}
    for(int i = 1; i < 10; i++) {}
}
当您声明一个for循环变量时,它仅在循环块内可见。这意味着两个变量的作用域是不重叠的,因为没有一行代码使得一个块“重叠”另一个块。

1
是的。当然,它会与第二个循环冲突,但这个也无法编译。int i;。但是,如何使用for (int i = 0; i < 10; i++) { } - M.kazem Akhgary
2
正如大多数答案所解释的那样,变量的作用域不是从其声明开始的,而是整个块。for循环定义了一个块。 - Panagiotis Kanavos
谢谢你的回答。所以它就像{int i; } { int i; }这样是可能的。 - M.kazem Akhgary
@M.kazemAkhgary 是的,你可以这样看待它。 - Kapol
5
等等,这一切不仅意味着局部变量不能遮蔽外部变量,而且声明的作用域是整个块而不仅仅是它被编写的地方,还有一个往前的概念吗?简单来说,这意味着 for (int i; ...) int i;int i; for (int i; ..) 是相同的。请您确认是否需要翻译其他内容。 - Bakuriu

8

for循环的作用域,for(INIT; COND; INCR) { BLOCK }与以下代码段中的作用域是相同的:

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

因此,for循环最好被认为是两个嵌套的作用域。(注意:上述从forwhile的转换未能正确捕获continue的行为。然而,这个问题并不是关注于它) 在for循环外部声明int i会遇到一个叫做“shadowing”的问题。在C++中,如果你声明了与外部作用域中某个变量同名的作用域变量,则会“shadowing”,直到该作用域结束时才会默默地覆盖它。当开发C#时,他们觉得这太反直觉,太容易出错了。在C#中,阴影变量来自外部作用域是语法错误。通过将int i引入到外部作用域中,现在对于for循环自己引入它是非法的。

很棒的工作,提到了替代作用域。我想在我的答案中包含这个细节,但我不记得for具体怎么翻译了。 - Kapol

3

在for循环中声明的变量仅在for循环块内部具有作用域,但是当您在for循环外部声明一个变量时,您不能在for循环内部拥有相同名称的变量,因为这会让编译器困惑,不知道您在for循环体中指的是哪个变量。

就像我将使用您的代码作为示例:

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
}

所以,如果你像你所做的那样在循环之间声明它,变量i在两个for循环中都具有作用域,因此在两个for循环中都可以访问它,因此如果你删除第一个循环,它仍然会因为变量在循环外部的全局作用域而发出警告:
for (int i = 0; i < 10; i++) 
{
  i = i+1; // now compiler is still confused which i you mean 
}
int i =0;

这不是事实,你搞错了。OP说它与第一个循环中的i声明冲突。你应该尝试编译OP的代码。 - Shaharyar
1
for (int i = 0; i < 10; i++) { } int i = 20; 为什么编译器会感到困惑? - Praveen
1
无论循环之前还是之后编写,它都是全局的。重点是循环外部的变量对两个循环都具有全局作用域。 - Ehsan Sajjad
2
@Shaharyar,只要'I'在相同的作用域中声明,它在哪里声明并不重要。它可以在之前或之后,C#不是从上到下编译的。 - Ryan Searle
1
@RyanSearle 谢谢你让我学到了新知识 :) - Shaharyar
显示剩余9条评论

2
在第二种情况下,您定义了一个外部的i,并在每个循环中尝试重新定义它。在for语句中声明的变量是局部变量,但您已经在外部作用域中定义了另一个同名变量。变量的作用域是定义它的块,不受其位置的影响。虽然编译器会拒绝在声明之前使用变量,但它仍然在作用域内。

1
实际上,OP的代码与第一个for循环有冲突,你没有对此进行任何解释。 - Shaharyar
@Shaharyar,实际上我写了关于内部和外部循环的内容,而不是第一或第二个。外部循环与两个循环都有冲突。注释其中任何一个,你会发现你仍然有冲突。这就是为什么我没有指定其中一个循环的原因。 - Panagiotis Kanavos
在某种程度上,变量在声明之后才会“在作用域内”。在声明之前不能使用 i。这种行为可能是编译器团队最简单的实现方式。编译器非常难写,所以我不怪他们。 - RubberDuck

2

我认为把int i;放在哪里并不重要。

编译器首先扫描字段,然后开始扫描表达式。它无法编译,因为i已被识别为字段。


1

int i; 在循环外声明的变量可以在当前函数中使用。要么只声明外部的 int i,并从两个循环中删除 int i,或者只删除这个外部变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接