#C 语言的作用域
作用域是指程序中定义的变量(或其他标识)符的可见性和生命周期范围,决定了变量(或其他标识)可以在哪些位置被使用。
在 C 语言中,任意一对花括号({})就是一个独立的作用域,在作用域中定义的变量称为该作用域的 局部变量(Local Variables)。
变量在定义时占用相应的内存空间,在离开作用域时其占用的内存被释放。
#函数作用域
典型的作用域环境就是函数。例如:
void foo()
{
int x = 10; // x 是函数 foo 内的局部变量
int y = 20; // y 是函数 foo 内的局部变量
}
void bar()
{
int x = 6; // x 是函数 bar 内的局部变量
}
- 函数
foo中定义了两个局部变量x和y,函数bar中不能访问这两个变量 - 函数
bar中中定了局部变量x,虽然和函数foo中的变量x同名,但它们毫无关系,函数foo中不能访问这个变量
函数的形式参数也是该函数的局部变量。
#块作用域
条件语句、循环语句、分支语句等也是独立的作用域。例如:
int main()
{
// x 是函数 main 内的局部变量
int x = 1;
if (1)
{
// if 块作用域
// 作为 main 函数作用域的子作用域,可以访问 x
// y 是 if 块内的局部变量
int y = 2;
}
else
{
// else 块作用域
// 作为 main 函数作用域的子作用域,可以访问 x
// 不能访问 if 块作用域中的 y
// z 是 else 块内的局部变量
int z = 3;
}
while (0)
{
// while 块作用域
// 作为 main 函数作用域的子作用域,可以访问 x
// 不能访问 if 块作用域中的 y
// 不能访问 else 块作用域中的 z
// y 是 while 块内的局部变量,与前面 if 块中的 y 同名,但毫无关系
int y = 4;
}
for (int i = 0; i < 10; i+=1) // 这里定义的 i 也是 for 块作用域内的局部变量
{
// for 块作用域
// 作为 main 函数作用域的子作用域,可以访问 x
// 不能访问 if 块作用域中的 y
// 不能访问 else 块作用域中的 z
// 不能访问 while 块作用域中的 y
// z 是 for 块内的局部变量,与前面 else 块中的 z 同名,但毫无关系
int z = 5;
}
// 直接使用花括号创建块作用域
{
// 作为 main 函数作用域的子作用域,可以访问 x
// 不能访问 if 块作用域中的 y
// 不能访问 else 块作用域中的 z
// 不能访问 while 块作用域中的 y
// 在此之前访问的 x 是 main 函数前面定义的 x
// x 是块内的局部变量,与 main 函数前面定义的 x 同,但毫无关系
int x = 6; // 与外层变量同名,容易引起混淆,不建议使用
// 在此之后范围的 x 是此处新定义的 x
}
return 0;
}
- 函数
main中定义了局部变量x,函数内部的其它子作用域可以访问它 if块中定义了局部变量y,其它作用域不能访问它else块中定义了局部变量z,其它作用域不能访问它while块中定义了局部变量y,其它作用域不能访问它- 此处的
y与if中的y同名,但毫无关系
- 此处的
for块中定义了局部变量i和z,其它作用域不能访问它- 此处的
z与else中的z同名,但毫无关系
- 此处的
- 末尾处的块作用域中定义了局部变量
x,其它作用域不能访问它- 此处的
x与main函数前面定义的x同名,但毫无关系
- 此处的
注意,在末尾的块作用域中可以访问 main 函数前面定义的变量 x。因此:
- 在其内部定义
x之前,访问的x是main函数前面定义的x - 在其内部定义
x之后,访问的x是块内部新定义的x
这种与外部变量同名的局部变量被称为影子(Shadow)变量,由于极易引起混淆,因此建议避免使用。
#在 switch 中定义局部变量
分支语句和上述其它块一样是独立的作用域,但其存在一些特别之处。
下面代码是错误的:
int x = 2;
switch (x)
{
case 1:
int n = 10; // n 是 switch 块内的局部变量
break;
case 2:
printf("%d\n", n); // 访问局部变量 n
break;
}
这段代码从作用域规则上来看没有问题:在 switch 块中定义了局部变量 n,然后在作用域内访问它。
但当 x 的值是 2 是,case 1 不会被执行,因此变量 n 不会被定义,也就无法被访问。
因此,语法规定 switch 块作用域中不能直接定变量;如果需要定义变量,则必须使用花括号({})创建新的块作用域。
例如:
switch (x)
{
case 1:
{
int n = 10; // n 是子块内的局部变量,其它作用域不能访问它
break;
}
case 2:
{
// 这里不能范围 case 1 中的 n,但可以重新定义一个
int n = 20; // n 是子块内的局部变量,其它作用域不能访问它
printf("%d\n", n); // 访问局部变量 n
break;
}
}
case 1的块中定义了局部变量n,在case 2中不能访问它case 2中另外定义了一个局部变量n,它与case 1块中的n没有任何关系
#全局作用域
在所有块之外的作用域被称为全局作用域,全局作用域中定义的变量称为 全局变量(Global Variables)。
例如:
#include <stdio.h>
// 定义全局变量
const double PI = 3.1415926;
/*********************************************
* @brief 计算圆的周长
* @param r 半径
* @return 圆的周长
********************************************/
double circle_circumference(double r)
{
return 2 * PI * r; // 访问全局变量 PI
}
/*********************************************
* @brief 计算圆的面积
* @param r 半径
* @return 圆的面积
********************************************/
double circle_area(double r)
{
return PI * r * r; // 访问全局变量 PI
}
int main(void)
{
double r = 10.0;
double circumference = circle_circumference(r);
double area = circle_area(r);
printf("半径为 %f 的圆,周长为 %f,面积为 %f\n", r, circumference, area);
return 0;
}
运行结果:
半径为 10.000000 的圆,周长为 62.831852,面积为 314.159260
这个示例中定义了全局变量 PI,在函数 circle_circumference 和 circle_area 均可访问该变量。