现在的位置: 首页 > 综合 > 正文

小谈变量作用域!

2012年11月24日 ⁄ 综合 ⁄ 共 1666字 ⁄ 字号 评论关闭
之前写程序只是知道,变量在使用前要初始化,但是却没有仔细地去想过究竟是为什么,直到昨天偶然间遇到了下面这样的问题:
        static void Main(string[] args)
        
{
            
int val;

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


            
//{ val = 1; }

            
// 这里会提示没有赋值的局部变量
            
//Console.WriteLine(val);

            Console.ReadKey();
        }

提示:使用了未赋值的局部变量"val"

那么和下面这段代码相比较:

        static void Main(string[] args)
        
{
            
int i;

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


            Console.WriteLine(i);

            Console.ReadKey();
        }

同样变量 int i 也是声明于for循环的外部,但是最后却可以通过Console.WriteLine()来输出这个i的值。通过查看反编译的IL代码发现,其实这个变量 i 是在for结构的循环体外部被初始化的,IL代码如下:

.locals init ([0] int32 i,
           [
1bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.
0 // 出栈
  IL_0002:  stloc.0 // 压栈,将0赋给变量i
  
// 下面才进入循环体
  IL_0003:  br.s       IL_000b
  IL_0005:  nop
  IL_0006:  nop
  IL_0007:  ldloc.
0
  IL_0008:  ldc.i4.
1
  IL_0009:  add
  IL_000a:  stloc.
0
  IL_000b:  ldloc.
0
  IL_000c:  ldc.i4.s   
10
  IL_000e:  clt
  IL_0010:  stloc.
1
  IL_0011:  ldloc.
1
  IL_0012:  brtrue.s   IL_0005
  
// 循环结束
  IL_0014:  ldloc.0
  IL_0015:  call       
void [mscorlib]System.Console::WriteLine(int32)

而上面的例子中,变量 int val 实际上是在for结构的循环体内部初始化的,通过查阅《C#入门经典》找到了答案,下面引用书中的原文:
“实际上任何变量,只声明一个简单的变量类型,并不会引起其他的变化,只有在给变量赋值后,这个值才占用一块内存空间。如果这种占据内存空间的行为在循环中发生,该值实际上被定义为一个局部值,在循环的外部会超出其作用域,从而失去作用。在循环外部赋值可以确保该值是主体代码的局部值,在循环内部它仍处于作用域中。”

比较奇怪的时,在上面第一段代码中,如果注释掉Console.WriteLine(val);这句编译可以通过,并且调试时发现,在for循环结束后仍然可以通过“即时窗口”调到该变量val的值。(如图)

实际上CLR允许任何语法正确的代码,其实这个只是C#编译器的逻辑检查功能罢了,它保证了程序的正确性,它认为FOR可以一次也不循环,也就有可以变量没赋值就使用,这样程序也就有可能会出现程序员不预期的效果,这是编译器的辅助功能,当然也可以通过设置编译器选项,降低编译器的代码安全检查标准来避免这个问题,但是一般来说并不推荐这样做。

抱歉!评论已关闭.