作用域 = 一个变量的"地盘",也就是"在哪些代码里叫这个名字能叫得应"。C++ 里主要有三层:全局(整个文件可见)、局部(只在本函数里)、块内(只在那一对 {} 里)。内外同名时,里面的说了算——这叫就近遮蔽。
6.3.2/6.3.3 一直在说"每层调用都新开一套变量",这一节换个角度把这件事讲透——名字的可视范围。旧知识也来帮忙:2.1.5 讲函数体时就说过"花括号是作用域边界",这一节把它展开成完整规则;6.2.2 的"形参是函数自己的盒子"是同一个道理。
一、变量的"地盘"有多大(三层)
| 作用域 | 在哪定义 | 谁能看见 | 活多久 |
|---|---|---|---|
| 全局 | 所有函数外面 | 从定义那行起,到文件结束(谁都能用) | 程序从头活到尾 |
| 局部 | 函数体里 | 只在本函数内 | 函数被调用时出生,函数一结束就消失 |
| 块内 | {} 里(if / for / while 的花括号) |
只在这个 {} 内 |
出这个花括号就消失 |
记忆法:作用域像小区的门禁——全局是"大门口,谁都能进",局部是"本单元楼",块内是"家里的一间屋"。
二、就近遮蔽(最容易踩的规则)
#include <iostream>
using namespace std;
int score = 90; // 全局
int main() {
cout << score << endl; // 90(看见全局的)
int score = 60; // 局部:把全局那个"挡住"了
cout << score << endl; // 60(看见局部的)
{
int score = 30; // 块内:又把外面的挡住
cout << score << endl; // 30
}
cout << score << endl; // 60(回到局部那个)
return 0;
}
规则一句话:从里往外找,找到第一个就停。里面的同名变量会把外面的遮住,但外面那个变量的值本身一点没变(和 6.2.2"复印件不影响原件"是同一种感觉)。
编译器通常会提示:局部变量遮蔽了全局变量时,多数编译器会给出 shadow 警告。见到这个警告就取个别的名字,别硬扛。
三、动手 Demo:三层变量的现场
#include <iostream>
using namespace std;
int count = 100; // 全局
void show() {
cout << "show 里能看到 count = " << count << endl; // 100(全局)
int count = 5; // 局部(遮蔽)
cout << "show 里遮蔽后 count = " << count << endl; // 5
}
int main() {
cout << "main 里 count = " << count << endl; // 100
show();
for (int i = 0; i < 1; i++) {
int count = 1; // 块内(再遮蔽)
cout << "循环块里 count = " << count << endl; // 1
}
cout << "回到 main,count = " << count << endl; // 100
return 0;
}
看到什么算成功:
main 里 count = 100
show 里能看到 count = 100
show 里遮蔽后 count = 5
循环块里 count = 1
回到 main,count = 100
重点看第一行和最后一行:不管中间怎么遮蔽,main 里看到的始终是全局那个 100。遮蔽只改变"看得见哪一个",不改变"别的变量有没有被改"。
30 秒实验:在 main 的最前面加一句 count = 200;(改的是全局那个),然后运行——show 里第一个输出也变成 200 了。这就是全局变量"人人可改"的威力,也是它的危险所在(谁都能改,改坏了难追责)。
四、全局变量的代价(为什么尽量不用)
| 好处 | 代价 |
|---|---|
| 随处可用,省得传参 | 谁都能改——一处改坏,全程序遭殃,还很难定位 |
| 生命周期长 | 常驻内存,程序结束才释放 |
| —— | 名字容易撞(各处命名都要躲着它) |
实用建议三条:
1. 能不用全局就不用。数据靠"参数传进去 + 返回值带出来"(第六章的正道)。
2. 非要用就加 const(2.2.5):const double PI = 3.14159;——只读的全局变量是安全的,它其实就是"常量"。
3. 命名上打个标记(如 g_count),提醒自己"这是全局的"。
五、常见误区
- "函数里定义的变量,函数外面也能用"——不能。局部变量一出函数就消失,外面既看不见也摸不到。
- "同名就是同一个变量"——只有同一作用域里的同名才是同一个;跨作用域的同名是两个不同的变量。
- "在 if 里定义的变量,if 结束后还能用"——不能。
for (int i = 0; ...)里的i出了循环就没了(5.2.1 讲过的"案发现场")。 - "全局变量比参数方便"——短期方便,长期灾难。参数和返回值才是函数之间通信的正规通道。
自测三问(点开对答案)
1. 作用域的三层分别是什么?——全局(整个文件)、局部(本函数)、块内(那一对花括号里)。 2. 什么是就近遮蔽?——内外同名时,从里往外找,第一个找到的就是生效的那个。 3. 为什么不推荐用全局变量?——谁都能改,改动难追溯;优先用参数和返回值传递数据。- ☐ 6.5 重载函数(下一节)
相关笔记
- 递归调用 —— 每层调用都新开一套变量
- 函数体 —— "花括号是作用域边界"的出处
- for循环语句的一般形式 ——
i出了循环就没了 - 🔷 C++基础 · 索引 —— 全课路线图