#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
均可访问该变量。