从本质上讲,变量是程序中用来存放信息的一块空间。“这块空间”一词,还要加两个定语。一是所存放的内容是可以(通过代码来)改变的;二是它的地址是可以访问的(否则就无法用代码来访问它)。这就引出一连串与变量有关的许多问题。
变量有两个属性。一是它的数据类型,就是这个空间能用来存放哪种类型的数据;二是它的时空属性。本文只涉及及后者。时就是变量的生命期,空就是变量的作用域。
在C中有两类变量:在任何函数外面定义的外部变量和在某函数内定义的自动变量。
尽管在大多数C书里大谈特谈全局变量,但是,事实上,在C标准里,从没定义过全局变量。C的创建者这么安排,是有用意的。这体现了C创建者的一个重要思想:尽一切可能限制变量的作用域。他们想限制的还有变量的生命期。这一思想贯穿了整个C标准,也成为后来才出现的结构化程序设计思想的重要内容之一。
外部变量是源文件级变量。就是说,它的作用域仅限于定义它的那个源文件。在源文件A里定义的一个外部变量,你要在源文件B里访问它,必须在文件B的开头用关键字extern声明它。注意定义和声明两个词,在经典的C书里,是很小心地区别开的。定义的实质是创建,本质上就是要申请一块存储空间。而声明不引发存储分配。这里的声明,只是使源文件A里的某变量的作用域延伸到源文件B里。
在一个工程里,外部变量必须在一个源文件里定义,在其它也需访问它的每个源文件里要extern声明它。
在工程里,如果你在某源文件里定义一个变量,并在其它所有源文件里声明它,这个变量就成了全局变量。因此,在C里,全局变量是靠你自己做出来的。
在小组开发中,如果你不希望你定义的外部变量被其他人访问,可以在定义时加上修饰词static。这样,这个外部变量的作用域就仅限于定义它的那个源文件了。如果在其它源文件里用extern声明它,编译时就会报错。一个外部静态变量就是一个真正的文件级变量。
外部变量的生命期是工程的整个运行期间(这里仅仅指静态分配的变量,动态分配的不一样。)。有些书里称之为“永久”。
自动变量分两级。在函数头部定义的是函数级,作用域是整个函数;在代码块头部定义的是代码块级,作用域仅限这个代码块。自动变量在机器执行到定义它的那个函数(或代码块)时被创建,离开函数(或代码块)时自动消失。再次进入该函数(或代码块)时,被再次创建。这就是“自动”的意义。因此,自动变量的生命期仅限该函数(或代码块)的一次运行过程。如果在定义时加修饰词static,就成了自动静态变量。自动静态变量的作用域不变,生命期变成永久(整个工程运行期)。自动静态变量必须有一个初值,作为该函数(或代码块)在第一次运行时变量的初值。以后的运行,每一次的初值都是上一次的终值。
不同作用域的变量允许同名。如果两个同名变量的作用域互不相交(譬如分处两个源文件的的两个外部静态变量),那当然互不搭界。如果一个作用域包含另一个作用域,则在程序运行时,会发生屏蔽现象。当进入内层时,外层的变量被内层的同名变量屏蔽。即这时实际访问的是内层变量。当内层终止时,外层变量便又显露出来。
在C++里,不再限定变量只能在代码块的首部定义。这有什么意义呢?假如,在一个代码块里需要定义一个变量,但是那个变量只是在代码段快终结处才被使用。如果在变量将使用时才定义它,就可以最大限度地限制它的作用域。另外的好处是代码可读性更好。因为如果代码块很大,定义和引用贴在一起,有利于程序的理解。新的C标准也扩充了这一点。