4360

22 分钟

#C 语言的作用域

作用域是指程序中定义的变量(或其他标识)符的可见性和生命周期范围,决定了变量(或其他标识)可以在哪些位置被使用。

在 C 语言中,任意一对花括号({})就是一个独立的作用域,在作用域中定义的变量称为该作用域的 局部变量(Local Variables)

变量在定义时占用相应的内存空间,在离开作用域时其占用的内存被释放。

#函数作用域

典型的作用域环境就是函数。例如:

void foo() { int x = 10; // x 是函数 foo 内的局部变量 int y = 20; // y 是函数 foo 内的局部变量 } void bar() { int x = 6; // x 是函数 bar 内的局部变量 }
  • 函数 foo 中定义了两个局部变量 xy,函数 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

y

if 块

z

else 块

y

while 块

z

for 块

x

无名块

i

  • 函数 main 中定义了局部变量 x,函数内部的其它子作用域可以访问它
  • if 块中定义了局部变量 y,其它作用域不能访问它
  • else 块中定义了局部变量 z,其它作用域不能访问它
  • while 块中定义了局部变量 y,其它作用域不能访问它
    • 此处的 yif 中的 y 同名,但毫无关系
  • for 块中定义了局部变量 iz,其它作用域不能访问它
    • 此处的 zelse 中的 z 同名,但毫无关系
  • 末尾处的块作用域中定义了局部变量 x,其它作用域不能访问它
    • 此处的 xmain 函数前面定义的 x 同名,但毫无关系

注意,在末尾的块作用域中可以访问 main 函数前面定义的变量 x。因此:

  • 在其内部定义 x 之前,访问的 xmain 函数前面定义的 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_circumferencecircle_area 均可访问该变量。

创建于 2025/8/1

更新于 2025/8/1