在C语言中,循环内声明的变量是局部变量吗?

7
    #include <stdio.h>

    int a;

    void myproc()
    {
        int a = 2;
        while (a == 2)
        {
            int a = 3;
            printf("a = %d\t", a);
            break;
        }
        printf("a = %d\t", a);
    }

    int main()
    {
        a = 1;
        myproc();
        printf("a = %d\t", a);
        return (0);
    }

我原本期望上述代码会输出:a = 3 a = 3 a = 1 然而,它输出的是:a = 3 a = 2 a = 1。请问有人能够提供一个合理的解释吗?

1
恭喜您提出了一个好问题。 - squiguy
6个回答

6

是的,它们是本地自动变量,并且在进入和退出给定作用域时将它们推入和弹出堆栈,除非编译器决定进行某些优化(例如将它们存储在寄存器中等)。但对于给定的变量,在访问它时使用最局部作用域的版本。例如,在C89中,如果您决定在for循环声明中声明循环计数器,则以下通常会产生编译错误:

for (int i=0; i < N; i++)
    for (int i=0; i < J; i++)
        printf("%d", i);

打印的i值将始终是在内部for循环中声明的i的值,因为它是最本地作用域的版本。


为什么会产生编译器错误?在我看来它是有效的。 - Nate
我的错误,在C89中你不能在for循环中声明自动变量,但是在C99中可以。 - Jason

6

以下是解释--请看下面的评论。

#include <stdio.h>

int a;

void myproc()
{
    int a = 2; // (1) a = 2
    while (a == 2) // true
    {
        int a = 3;  // (2) new scope, new a = 3
        printf("a = %d\t", a); // (X) prints 3 as it gets the 'nearest a' in the scope
        break;
    } // throws away a=3 from (2)
    printf("a = %d\t", a); // (Y) Uses (1) i.e. 2 and print it 
}

int main()
{
    a = 1;
    myproc();
    printf("a = %d\t", a); // (Z) Just prints 1 as the scope is not effected by myproc
    return (0);
}

因此,这将打印出(X)、(Y)和(Z)

即3 2 1


你使用 while (a == 2) ... break; 而不是 if (a == 2),有什么特别的原因吗? - Brendan
哈哈哈,我明白了,抱歉。 - Brendan

3

"在循环内"?

你所问的问题与任何循环完全没有关系。你代码中的内容和普通的代码没有区别。

int a;

void myproc()
{
    int a = 2;
    {
        int a = 3;
        printf("a = %d\t", a);
    }
    printf("a = %d\t", a);
}

每个嵌套块都有自己的变量。这就是全部内容,和任何循环无关。
真正与循环相关的声明应该是在循环头部进行的声明,例如:
    int a = 3;

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

    printf("a = %d\n", a); // <- prints `3`

for头中声明的a仍然局限于循环内部。


非常感谢你,安德烈,那真是启发性的!! - hardcoder

2

{ } 变量声明作用域。当您在这些大括号之间声明变量时,它只能在这些大括号中使用。如果您有嵌套变量(比如在 myproc 中,您声明 a = 2,然后在循环内部 a=3),则最靠近当前作用域的变量是被引用的(在本例中,a=3)。

由于您的打印语句嵌套在 {} 中,因此只会打印最近声明的变量 a,从而得到您的结果。


1

每当你在循环内声明一个变量时,该变量就不可在外部使用(超出该变量的作用域)。因此,在你的代码中

 void myproc()
{
    int a = 2;
    while (a == 2)
    {
        int a = 3;// This is not accessable to outside while loop. 
        printf("a = %d\t", a);
        break;
    }
    printf("a = %d\t", a);
}

如果你想打印 3、3、1,请在 while 循环中删除 int :)


1

实际上,在这里循环结构是一种相当方便的混淆器。

考虑 while 循环的测试条件。在程序执行期间,该测试会运行两次,并且第一次测试成功,因为它正在检查的变量 a 的值为 2。第二次运行时,测试失败了,因为正在测试的变量 a 是不同的变量,它的值是 3!这非常令人惊讶。

根据我的同事们的解释,变量 a 的三个声明只存在于它们包含的“{”和“}”限定范围内(或者第一个变量在全局范围内)。如果这完全是真的,那么 while (a == 2) 测试条件应该永远通过,因为分配给值为 3 的内部变量 a 被“{”和“}”完全隐藏了。

int a = 1;         // world scope - a is 1
myproc() 
 {                 // New scope; can define names inside here
  int a = 2;       // Redefine a to be 2
  while ( a == 2 ) // We test whether a is 2
  {                // New scope; can define names inside here 
    int a = 3;     // Redefine a to be 3  
  }                // end of scope, the a = 3 disappears; also branch back to top of loop
 }                 // end of myprog scope, so the a=2 disappears

理解这一点的方法是意识到while (test) { statements; }实际上是这样实现的:
if ( test ) {      // T1
   L1: {
      statements;
      if ( test )  // T2 
          goto L1;
   }
} 

实际上,测试语句被复制,第一个语句'T1'在'{'和'}'之外的范围内执行,并获得a为2并通过;第二个语句'T2'在'{'和'}'的范围内执行,并获得a为3,因此测试失败。

根据您的语句,第二个测试使用其本地作用域定义的a,该值为3,因此循环在一次通过后退出。


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