在for循环中,计数器变量的作用域是什么?

10

我在Visual Studio 2008中遇到了以下错误:

Error 1 A local variable named 'i' cannot be declared in this scope because it would give a different meaning to 'i', which is already used in a 'child' scope to denote something else

这是我的代码:

for (int i = 0; i < 3; i++)
{
  string str = "";
}

int i = 0; // scope error
string str = ""; // no scope error

我知道一旦循环结束,str 就不存在了,但我也认为 i 的作用域仅限于 for 循环内部。

那么 i 的作用域与刚好在 for 循环外声明的变量相同吗?

编辑:

为了明确,我正在使用 C#。我正在考虑删除 "C" 标签。但是,由于正确的答案解释了两者之间的区别,所以我认为保留两个标签是有意义的。

我上面的代码评论中有一个错误:

for (int i = 0; i < 3; i++)
{
  string str = "";
}

int i = 0; // scope error
string str = ""; // also scope error,
                 // because it's equivalent to declaring
                 // string str =""; before the for loop (see below)

2
你已经标记了c#和c。你在问哪种语言?答案可能会因为你指的是哪种语言而不同。 - James McNellis
2
请参考Eric Lippert在https://dev59.com/aHE85IYBdhLWcg3wnU0d中的精彩回答。另外,还可以查看https://dev59.com/unM_5IYBdhLWcg3w2XLw和https://dev59.com/InI95IYBdhLWcg3w_zWs。 - jloubert
1
只是为了明确,如果您最初是以“我在C# 3.0(Visual Studio 2008)中遇到以下错误...”开始的,然后是“C#的for循环作用域规则与ANSI C有何不同?”,这似乎是您的意图。那么将此标记为c#c(或c ++)没有问题。但是,在原始问题的任何地方都没有指定您使用的语言,在这种情况下,大多数人会查看标签...这就是两个语言标签的问题所在。 - Ben Voigt
4个回答

21

我认为你们都混淆了C++和C#。

在C++中,以前在for表达式中声明的变量作用域是在其后面的块之外的。但已经有一段时间将其更改,使得在for表达式中声明的变量作用域是在其后面的块内部。 C#遵循这种后一种方法。但两者都与此无关。

这里发生的事情是,在C#中,一个作用域不允许隐藏一个外部作用域中具有相同名称的变量。

因此,在C++中,这以前是非法的。现在合法了。

for (int i; ; )
{
}
for (int i; ; )
{
}

在C#中,这也是合法的。有三个作用域,外层作用域中未定义'i',而两个子作用域分别声明了自己的'i'。

但你正在做的是这个:

int i;
for (int i; ; )
{
}

这里有两个作用域。一个外部作用域声明了一个“i”,另一个内部作用域也声明了一个“i”。在C++中这是合法的,因为外部“i”被隐藏了,但是在C#中无论内部作用域是否是for循环、while循环或其他任何东西,都是非法的。

试试这个:

int i;
while (true)
{
    int i;
}

这是同样的问题。C#不允许在嵌套作用域中使用相同名称的变量。


10
这是正确的答案。C#不允许隐藏变量,因为这可能会导致混淆。C++则无所谓。这与for循环语法、变量是在函数顶部还是底部声明或其他任何事情都没有关系... - John Kugelman
1
我发现有时候实际阅读错误信息是有帮助的。 - Jeff Dege

3

for循环结束后,增量器不再存在。

for (int i = 0; i < 10; i++) { }
int b = i; // this complains i doesn't exist
int i = 0; // this complains i would change a child scope version because the for's {} is a child scope of current scope

你无法在 for 循环后重新声明 i 的原因是,在 IL 中它实际上会在 for 循环之前声明,因为声明发生在作用域顶部。


+1 Hoffa:我猜你已经弄清楚了,但是你为什么删除了你的示例代码呢?那是一个很好的观点。 - JohnB
@JohnB:我之前错误地声明了错误,现在已经修复了。有时候只需要仔细思考这些问题 :) - Jimmy Hoffa
@JohnB: 请说明您使用的编程语言。 - Luca Matteis
@Luca Matteis:我使用的是C#,但我认为这并不重要! - JohnB
Hoffa也指出变量声明的顺序是可逆的,这是个好观点,但Jeff Dege给出了真正的答案。 - JohnB

2

一些背景信息:顺序并不重要。只有作用域的概念——方法作用域和for循环的作用域。因此,“一旦循环终止”不准确。

因此,您的帖子与以下内容相同:

int i = 0; // scope error
string str = ""; // no scope error

for (int i = 0; i < 3; i++)
{
  string str = "";
}

我发现这样思考可以更好地使答案“契合”我的心理模型。

+1 谢谢和非常好的观点!这更清楚了!(但并没有直接回答我的问题,所以你没有得到勾选) - JohnB
1
在C++中,str的双重声明是可以的,但在C#中却是作用域错误。OP问题中的问题不在于在同一作用域中声明了两个变量,而是因为C#不允许您在嵌套作用域中拥有相同的变量,因为这可能会令人困惑。C++允许您做这种令人困惑的事情。 - John Kugelman
1
约翰,当然,你说了什么让我的回答不合适或无用(假设你投票反对我)?正如我所说的,这是一些背景信息,正如OP所说,“谢谢和很好的观点!这更清楚地解决了问题”。 - Kieren Johnstone
1
这两个 str 变量在 C# 中确实会导致错误,就像两个 i 变量一样。而在 C++ 中,两者都不会导致错误。你的代码片段在任何一种语言中都是错误的。我不是要加重负担,但我认为你的答案增加了提问者的困惑,而不是解决它。 - John Kugelman
@John Kugelman:最初我说“string str = ""; // no scope error”是我的错。然而,Kieren真正解释了我为什么首先会出现错误(因为序列对编译器是可逆的)。不过,我将检查给Jeff Dege,因为他提供了更清晰的答案。 - JohnB
@John Kugelman - 不,我并没有说那是有效的代码。请再读一遍问题。我说“因此,您的帖子读起来与此相同”。原始帖子有错误,所以我重新列出了相同的代码,但是将行移动了。我并没有提供解决方案。在您决定投票反对之前,请重新阅读答案;您是错误的。 - Kieren Johnstone

2

是的。从语法上来说,新的作用域在由花括号定义的块内部。从功能上来说,有些情况下您可能需要检查循环变量的最终值(例如,如果您使用了break语句)。


+1 右边,在 {} 内部,这完全是有意义的,并且使其保持一致! - JohnB

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