西红柿小生|C语言之两种作用域:函数的作用域与文件作用域


西红柿小生|C语言之两种作用域:函数的作用域与文件作用域
作用域描述程序中可访问标识符的区域 。 一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域 。
其中 , 块是用一对花括号括起来的代码区域 。 例如 , 整个函数体是一个块 , 函数中的任意复合语句也是一个块 。 定义在块中的变量具有块作用域(block scope) , 块作用域变量的可见范围是从定义处到包含该定义的块的末尾 。
【西红柿小生|C语言之两种作用域:函数的作用域与文件作用域】另外 , 虽然函数的形式参数声明在函数的左花括号之前 , 但是它们也具有块作用域 , 属于函数体这个块 。 所以到目前为止 , 我们使用的局部变量(包括函数的形式参数)都具有块作用域 。 因此 , 下面代码中的变量cleo和patrick都具有块作用域:
double blocky(double cleo){double patrick = 0.0;...return patrick;}声明在内层块中的变量 , 其作用域仅局限于该声明所在的块:
double blocky(double cleo){double patrick = 0.0;int i;for (i = 0; i < 10; i++){double q = cleo * i; // start of scope for q...patrick *= q;}// end of scope for q...return patrick;}在该例中 , q的作用域仅限于内层块 , 只有内层块中的代码才能访问q 。
以前 , 具有块作用域的变量都必须声明在块的开头 。 C99标准放宽了这一限制 , 允许在块中的任意位置声明变量 。 因此 , 对于for的循环头 , 现在可以这样写:
for (int i = 0; i < 10; i++)printf("A C99 feature: i = %d", i);为适应这个新特性 , C99把块的概念扩展到包括for循环、while循环、do-while循环和if语句所控制的代码 , 即使这些代码没有用花括号括起来 , 也算是块的一部分 。 所以 , 上面for循环中的变量i被视为for循环块的一部分 , 它的作用域仅限于for循环 。 一旦程序离开for循环 , 就不能再访问i 。
函数作用域(function-scope)仅用于goto语句的标签 。 这意味着即使一个标签首次出现在函数的内层块中 , 它的作用域也延伸至整个函数 。 如果在两个块中使用相同的标签会很混乱 , 标签的函数作用域防止了这样的事情发生 。
函数原型作用域(function prototype scope)用于函数原型中的形参名(变量名) , 如下所示:
int mighty(int mouse, double large);函数原型作用域的范围是从形参定义处到原型声明结束 。 这意味着 , 编译器在处理函数原型中的形参时只关心它的类型 , 而形参名(如果有的话)通常无关紧要 。
而且 , 即使有形参名 , 也不必与函数定义中的形参名相匹配 。 只有在变长数组中 , 形参名才有用:
void use_a_VLA(int n, int m, ar[n][m]);方括号中必须使用在函数原型中已声明的名称 。 变量的定义在函数的外面 , 具有文件作用域(file-scope) 。 具有文件作用域的变量 , 从它的定义处到该定义所在文件的末尾均可见 。 考虑下面的例子:
#include int units = 0;/* a variable with file scope */void critic(void);int main(void){...}void critic(void){...}这里 , 变量units具有文件作用域 , main()和critic()函数都可以使用它(更准确地说 , units具有外部链接文件作用域 , 稍后讲解) 。 由于这样的变量可用于多个函数 , 所以文件作用域变量也称为全局变量(global variable) 。
翻译单元和文件
我们常常会认为多个文件在编译器中可能以一个文件出现 。 例如 , 通常在源代码(.c扩展名)中包含一个或多个头文件(.h扩展名) 。 头文件会依次包含其他头文件 , 所以会包含多个单独的物理文件 。


推荐阅读