“ for”循环中的“ for”循环可以使用相同的计数器变量名称吗?


107

可以在for循环内部的for循环中使用相同的计数器变量吗?

还是变量会互相影响?以下代码是否应该在第二个循环中使用其他变量,例如j,还是可以i

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

72
这令人困惑-在代码审查中不会超越我。但这是合法的。有两个不同的变量,都称为i,具有不同的作用域。-Wshadow与GCC配合使用可自动报告此类问题。
乔纳森·勒夫勒

15
我很惊讶它-Wshadow没有包含在其中-Wall
左右约

5
@leftaroundabout也-Wshadow警告有关全局变量的阴影,这在大型项目中可能很容易让人烦恼。
立方'18

9
@leftaroundabout更令人惊讶,甚至-Wextra不包含-Wshadow。我想这在某些项目中已经足够普遍了,或者某些gcc开发人员喜欢将阴影作为一种编码样式,以确保像这样被排除在外。
海德

5
@leftaroundabout与Cubic所说的相呼应,-Wshadow其误报率极高,使其完全无用。范围之所以存在是有原因的,而阴影是先验的,没有问题。现在-Wshadow-local(注:不是 -Wshadow=local)非常不同。但不幸的是,到目前为止,GCC拒绝将其包含在主干中(尽管似乎有些GCC的分支确实包含了它)。
康拉德·鲁道夫

Answers:


140

您可以使用相同的名称(标识符)。这将是一个不同的对象。它们不会互相影响。在内部循环内部,无法引用在外部循环中使用的对象(除非您对此进行了特殊规定,例如通过提供指向它的指针)。

这通常是不良样式,容易造成混乱,应避免使用。

仅当内部对象分别定义时,对象才不同,如int i您所显示的那样。如果使用相同的名称而不定义新的对象,则循环将使用相同的对象,并且会相互干扰。


3
在i ++内嵌套使用for(i)和for(j),将增加外循环变量。但是,如果在两个循环中使用相同的标识符,则您说的是正确的,因为它们是范围不同的变量。
KYL3R

3
@BloodGain:“对象”是C标准中使用的技术术语。我在这里故意使用它。
埃里克·波斯特皮希尔

1
@EricPostpischil:啊,我知道了。我没有意识到标准中的定义,并且担心它会误导新程序员(因为这很显然是一个初学者的问题),因为在我们通常使用术语C的意义上,C没有“对象”。我在C11标准中看到了它,现在我很好奇它是否在C11之前以这种方式定义。
Bloodgain '18

1
它是。在C99标准中为3.14,而不是3.15。所以我没有任何借口。那会教我问你<:-|
Bloodgain '18

1
更笼统地说:没有什么可以阻止您在任何嵌套作用域中重用变量名。当然,除了担心编写令人困惑的代码而惧怕上帝的惩罚。
艾萨克·拉比诺维奇

56

首先,这是绝对合法的:代码将编译并运行,并重复嵌套循环的主体10×10 = 100次。i嵌套循环内部的循环计数器将隐藏外部循环的计数器,因此这两个计数器将彼此独立地递增。

自外 i是隐藏的,因此嵌套循环体内的代码只能访问嵌套循环的值,i而不能i访问外部循环。在嵌套循环不需要访问外部i代码的情况下,此类代码可能是完全合理的。但是,这很可能会使读者感到困惑,因此,最好避免编写此类代码以避免“维护责任”。

注意:即使两个循环的计数器变量具有相同的标识符i,它们仍然是两个独立变量,即您不是在两个循环中使用相同的变量。在两个循环中使用相同的变量也是可能的,但是代码将很难阅读。这是一个例子:

for (int i = 1 ; i < 100 ; i++) {
    for ( ; i % 10 != 0 ; i++) {
        printf("%02d ", i);
    }
    printf("%d\n", i);
}

现在,两个循环使用相同的变量。但是,要花一些时间才能弄清楚该代码的功能而不进行编译(demo)。


4
由于问题被表述为“使用相同的计数器变量”,所以我也要指出,仅在重新定义发生时才会出现阴影。省略int内部for循环,即实际上使用相同的计数器变量,将导致外部循环仅运行一次,因为内部循环将离开i == 10。这是微不足道的,但是考虑到问题的表达方式,它认为它可以提供澄清
Easton Bornemeier

@EastonBornemeier您说得对,我认为我应该在答案的正文中解决“相同变量”的问题。谢谢!
dasblinkenlight

@EricPostpischil“可变阴影”是一个正式术语,在Wikipedia上有其自己的页面。不过,我已经将答案更新为与标准的措辞一致。谢谢!
dasblinkenlight

2
@dasblinkenlight:实际上,我对方向有脑筋痉挛,内层名称确实掩盖了外层名称。在这方面,我先前的评论是错误的。我很抱歉。(但是,这是英语意义上的,不是官方意义上的-Wikipedia通常不是C或程序设计的官方出版物,并且我不知道定义该术语的任何办公室或权威机构。)C标准使用“隐藏”,因此更可取。
埃里克·波斯特皮希尔

很好,尤其是带有“相同变量”的示例。但是,我认为“ 代码将按预期的方式编译和运行 ”会比“ 经过仔细阅读并理解所有预期结果的可以编译和运行代码”更好……正如您所说,像这样的代码“ 可能会使读者产生更多的困惑 ”,而问题是,困惑的读者可能会期望它所做的事情之外的事情。
TripeHound18年

26

您可以。但是您应该知道is 的范围。如果我们称之为外ii_1和内ii_2,所述的范围iS是如下:

for(int i = 0; i < 10; i++)
{
     // i means i_1
     for(int i = 0; i < 10; i++)
     {
        // i means i_2
     }
     // i means i_1
}

您应该注意到它们不会互相影响,只是它们的定义范围不同。


17

这是完全可能的,但请记住,您将无法解决 第一个声明的i

for(int i = 0; i < 10; i++)//I MEAN THE ONE HERE
{

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

    }
}

在第二个子循环内的第二个循环中

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

  for(int i = 0; i < 10; i++)//the new i
    {
        // i cant see the i thats before this new i here
    }
}

如果需要调整或获取第一个i的值,请在第二个循环中使用j

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

  for(int j = 0; j < 10; j++)
    {

    }
}

如果您有足够的创造力,您可以一次完成两个任务

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}

6
如果在代码审查期间我在嵌套循环中发现了阴影i变量,我会将其视为指导机会。如果我发现有人像上一个例子那样混淆了内部循环(不是一个循环),我可能会将他们扔出一个窗口。
Bloodgain '18

它是一个循环,只有一个for循环,如果是2,则将有两个for关键字或两个while关键字或for and while关键字
Dodo

3
这就是为什么我说您混淆了循环。您仍然在循环,只是用不太明显的语法将其隐藏。而且在所有方面它都变得更糟。
Bloodgain

12

是的,您可以对内部for循环使用与外部for循环相同的计数器变量名称。

for循环

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
用作loop_statement的expression语句建立其自己的块范围,这init_clause 的范围不同

for (int i = 0; ; ) {
    long i = 1;   // valid C, invalid C++
    // ...
}  

的范围loop_statement嵌套的范围内init_clause

来自C Standards#6.8.5p5迭代语句[强调我的]

迭代语句是一个块,其作用域是其封闭块作用域的严格子集。循环主体也是一个块,其作用域是迭代语句作用域的严格子集

来自C Standards#6.2.1p4标识符的范围[强调我的]

....在内部范围内,标识符表示在内部范围内声明的实体;在外部范围内声明实体在内部范围内隐藏(不可见)。


10

从代码/编译器的角度来看,这将是一件完全有效且合法的事情。int i内部for(int i = 0; i < 10; i++)循环中的声明位于一个新的较小的范围内,因此声明遮盖int i外部循环中的声明(或换句话说:在内部范围中,对变量的所有访问都将i进入int i内部范围中的声明,离开int i外部范围不变)。

也就是说,从代码质量的角度来看,这是完全可怕的。它很难阅读,难以理解并且容易被误解。不要这样


8

是的,您可以使用它,但是很混乱。最重要的是循环内局部变量的范围。就在函数内部声明变量而言,该变量的范围就是该函数。

int a = 5;
// scope of a that has value 5
int func(){
    int a = 10;
   // scope of a that has value 10
}
// scope of a that has value 5

与循环类似,内部循环内部声明的变量具有不同的作用域,而外部循环声明的变量具有不同的作用域。

for(int i = 0; i < 10; i++){
    // In first iteration, value of i is 0

    for(int i = 1; i < 10; i++){
        // In first iteration, value of i is 1
    }
    // In first iteration, value of i is 0
}

更好的方法是对内部和外部循环使用不同的变量。

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

    for(int j = 1; j < 10; j++){

    }

}

8

是的,绝对可以使用相同的名称变量。

C编程变量可以在三个位置声明:
局部变量:-在函数或块内。
全局变量:-所有功能之外。
形式参数:-在功能参数中。

但是在您的情况下,您 i scope必须要注意以下几点

for(int i = 0; i < 10; i++)
{
     // i means 1st for loop variable
     for(int i = 0; i < 10; i++)
     {
        // but here i means 2nd for loop  variable
     }
     //interesting thing here i means 1st for loop variable
}

注意:最好的做法是对内部和外部循环使用不同的变量


6

是的-更有趣的是,每次打开一组花括号时,您都可以重用变量名。这在插入诊断代码时通常很方便。键入一个大括号“ {”,然后声明和使用变量,然后关闭大括号,变量消失。这样可以确保您不会干扰主体中的任何内容,同时仍然保留了在括号外声明的任何变量,类和方法的优势。


3

范围规则:在for语句中声明的变量只能在该语句和循环主体中使用。

如果在您的代码中在内部循环中定义了i的多个实例,则每个实例将占用其自己的内存空间。因此,不必担心结果会是相同的。

int main(void) {

    int i = 2; //defined with file global scope outside of a function and will remain 2
    if(1)
    {       //new scope, variables created here with same name are different
        int i = 5;//will remain == 5
        for(int i = 0; i < 10; i++)
        {   //new scope for "i"

            printf("i value in first loop: %d \n", i); // Will print 0 in first iteration
            for(int i = 8; i < 15; i++) 
            {   //new scope again for "i", variable with same name is not the same
                printf("i value in nested loop: %d \n", i); // Will print 8 in first iteration
            }
        }

    }

    return 0;
}

但是,不建议使用相同的变量名,因为它很难理解,并且以后会变成不可维护的代码。


1

重要的是内部循环参数包含int i。因为i是以这种方式重新定义的,所以两个变量不会相互影响。它们的范围是不同的。这是两个例子显示此:

for(int i = 0; i < 10; i++) // This code will print "Test" 100 times
{
 for(int i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

请注意,上面的代码包含int i在内循环参数中,下面的代码仅包含i

for(int i = 0; i < 10; i++) // This code will print "Test" 10 times
{
 for(i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

0

好吧,您可以在脚本没有问题的情况下执行此操作,但应避免使用这种结构。通常会导致混乱

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.