之前写程序只是知道,变量在使用前要初始化,但是却没有仔细地去想过究竟是为什么,直到昨天偶然间遇到了下面这样的问题:
小谈变量作用域!        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可以一次也不循环,也就有可以变量没赋值就使用,这样程序也就有可能会出现程序员不预期的效果,这是编译器的辅助功能,当然也可以通过设置编译器选项,降低编译器的代码安全检查标准来避免这个问题,但是一般来说并不推荐这样做。

转载于:https://www.cnblogs.com/cnxcfeng/archive/2008/05/24/1206389.html

相关文章: