作用域
一、作用域分类与同名变量遮蔽规则
1. 作用域的三大分类
(1)全局作用域
- 定义:在所有函数、代码块之外定义的变量/函数,拥有全局作用域。
核心特性:
- 在整个程序的所有函数、代码块中都可以访问(除非被同名局部变量遮蔽);
- 生命周期伴随程序全程,程序启动时创建,结束时销毁。
示例:
int x = 10; // 全局变量,全局作用域 void func() { int x = 20; // 局部变量,遮蔽全局x cout << x; // 优先访问局部变量,输出20 } int main() { func(); cout << x; // 无同名局部变量,访问全局变量,输出10 return 0; }
(2)局部作用域(函数作用域)
- 定义:在函数内部定义的变量,拥有局部作用域,也叫函数作用域。
核心特性:
- 仅在当前函数内部可访问,函数外部无法访问;
- 函数执行时创建,执行结束后销毁;
- 若与全局变量同名,局部变量会遮蔽全局变量,函数内默认访问局部变量。
(3)块作用域(复合语句作用域)
- 定义:在
{}包裹的代码块(复合语句)内定义的变量,拥有块作用域,是局部作用域的子集。 核心特性:
- 仅在当前
{}代码块内可访问,代码块结束后变量立即销毁; - 若与外层代码块、全局变量同名,内层块变量会遮蔽外层变量,块内默认访问内层变量。
- 仅在当前
示例:
int a = 1; // 全局作用域 void test() { int a = 2; // 函数局部作用域,遮蔽全局a { int a = 3; // 块作用域,遮蔽外层a a++; // 仅修改内层块的a,变为4,块结束后该变量销毁 } a++; // 修改函数局部的a,变为3 cout << a << " "; // 输出3 } int main() { test(); cout << a; // 输出全局的1 return 0; }
2. 核心规则:就近原则(名字遮蔽)
当不同作用域存在同名变量时,编译器会优先选择当前所在的最小作用域内的变量,外层、全局的同名变量会被遮蔽,默认无法直接访问。
- 访问优先级:内层块作用域 > 函数局部作用域 > 全局作用域。
二、全局作用域解析符 ::
1. 核心作用
当局部变量遮蔽了全局同名变量时,使用::变量名的格式,可以强制访问全局作用域内的变量,绕过就近原则的遮蔽效应。
2. 语法格式
::全局变量名3. 示例
int value = 100; // 全局变量
void print1() {
int value = 50; // 局部变量,遮蔽全局value
cout << value << " "; // 优先访问局部变量,输出50
cout << ::value << " "; // 强制访问全局变量,输出100
}
void print2() {
cout << value << " "; // 无同名局部变量,直接访问全局,输出100
}
int main() {
print1();
print2();
return 0;
}三、函数的作用域与前向声明规则
1. 核心规则
C++中,函数必须先声明/定义,后调用。函数的作用域从它的声明/定义位置开始,到程序文件末尾结束。
- 如果在调用函数时,编译器还没有看到该函数的声明或定义,会直接报编译错误:找不到函数。
2. 示例
// 错误写法:调用在前,定义在后,无前置声明
int main() {
greet(); // 调用点编译器未见过greet函数,编译报错
return 0;
}
void greet() { // 函数定义在调用之后
cout << "Hello!" << endl;
}3. 两种正确写法
(1)函数定义放在调用之前
// 先定义,后调用,符合规则
void greet() {
cout << "Hello!" << endl;
}
int main() {
greet();
return 0;
}(2)函数前向声明(声明在前,定义在后)
// 先做前向声明,告知编译器函数的存在
void greet();
int main() {
greet(); // 编译器已见过函数声明,调用合法
return 0;
}
// 后完成函数定义
void greet() {
cout << "Hello!" << endl;
}四、考点
1. 易错点1:局部变量同名初始化的未定义行为
错误代码
int x = 10; // 全局变量
void f() {
int x = x + 1; // 未定义行为,绝对不会输出11
cout << x << endl;
}
int main() {
f();
}错误原因
在局部变量int x的定义语句中,=右侧的x会被编译器优先匹配当前正在定义的局部变量x,而非全局变量x。此时局部变量x还未完成初始化,属于未定义行为,程序结果不可预测。
2. 易错点2:作用域 ≠ 生命周期
- 作用域:变量可以被访问的代码范围,是编译期的概念;
- 生命周期:变量在内存中存在的时间,是运行期的概念。
- 高频误区:块作用域内的变量在块结束后就被销毁,即使通过指针拿到了它的地址,也无法合法访问,会触发未定义行为。
3. 易错点3::: 仅能访问全局变量
:: 只能用于访问全局作用域的变量,无法用它访问外层函数、外层代码块中的同名局部变量。
4. 易错点4:全局变量的重复定义
全局作用域内,同名的全局变量只能定义一次,多次定义会触发编译错误;而不同局部作用域内的同名变量互不影响。
五、考点速记表
| 题目核心场景 | 核心规则 | 正确结论 |
|---|---|---|
| 全局与局部同名变量 | 就近原则,局部优先 | 函数内默认访问局部变量 |
| 嵌套块内同名变量 | 内层块优先遮蔽外层 | 块内仅修改内层变量,不影响外层 |
| 访问被遮蔽的全局变量 | 用::全局变量名强制访问 | ::value可获取全局作用域的value |
| 函数调用在前、定义在后 | 必须先声明/定义后调用 | 无前置声明会直接编译报错 |
| 局部变量定义时用自身初始化 | 优先匹配当前局部变量,未初始化 | 未定义行为,结果不可预测 |
评论已关闭