前言:很多新手容易把这三个概念混在一起:生存期管「内存在不在」,作用域管「理论上代码哪块区域有权限访问这个名字」,可见域管「当前代码里,这个名字真正能找到它、合法访问」。可见域是作用域的子集
一、定义
形参调用时分配内存,调用结束释放,对应三条:生存期、作用域、可见域
通俗地讲,生存期指的是在程序运行过程中,变量从创建到撤销的一段时间。生存期的长短取决于前面所讲的存储方式,对于自动分配(栈分配),变量与其所在的代码块共存亡,对于静态分配(编译器预分配),变量与程序共存亡,程序开始执行时即已存在,一直到程序运行完毕退出后才被撤销,对于动态存储的内存块(不是指向该内存块的指针),由程序员决定其生存期。
| 生存期 | 变量在内存里存活的时间段:从分配内存 → 内存被回收销毁 | 内存有没有存在 | 存储方式(自动 / 静态 / 动态) |
| 作用域 | 源代码文本区域,理论上这个标识符名字有效的范围(不考虑屏蔽) | 名字理论上在哪块代码生效 | 声明位置、static/extern |
| 可见域 | 源代码里真正能直接访问这个变量的区域(如果有同名内层变量屏蔽,这块区域就要切掉) | 名字现在能不能直接找到、拿来用 | 作用域 + 屏蔽规则 |
二、作用域
严格地说,作用域是程序正文代码(不包括注释等)中的一片区域,在这块区域内,理论上标识符(排除下面要介绍的屏蔽情况)指向同一内存单元,可以将C++作用域分为以下几类。
1、块作用域
块就是一对{ }包裹的代码(if、for、while、函数体、单独写的 {})
自动变量,内部静态变量和函数形参具都具有块作用域,在一个块内声明的变量,其作用域从声明点开始,到该块结束为止。
提示:强调goto语句,在本函数内给出的标号,不管在任何位置,都可使用goto语句将程序流程转到标号处,但是,C++不允许使用goto语句将流程转到别的函数中。
2、文件作用域
外部静态变量static具有文件作用域。
-
作用域:从声明行开始,一直到整个.cpp文件末尾
-
关键:仅限当前这个 cpp 文件,别的文件看不见这个变量,跨文件 extern 也拿不到它。
3、全局(程序)作用域
int x; 写在函数外面,不带 static
-
作用域:整个程序(多个 cpp 文件组成的项目)
-
但是⭐不是写了就自动到处可见!必须在要使用的文件里用 extern 引用声明,才能在别的文件里面访问它
4、类作用域
类作用域:写在class { }大括号里面的成员,只在这个类里面默认可见。
外面访问它,需要用 对象.成员 或者 类名::成员,这块属于面向对象内容,后面讲类的时候再深挖。
三、可见域
可见域是作用域的子集,我们可以这样理解:作用域指的是变量理论上有效的源代码区域,而可见域指的是该变量实际有效的内存区域,如果没有屏蔽发生,可见域和作用域应该是等价的。
1.屏蔽
#include <iostream>
using namespace std;
extern int A, B, C; // 引用声明,告诉编译器A、B、C是外面定义的全局变量
int main()
{
cout << "A:" << A << ",B:" << B << ",C:" << C << endl; //这里没有屏蔽,访问全局A B C
int B = 1; //局部B,块作用域,开始屏蔽全局B
cout << "A:" << A << ",B:" << B << ",C:" << C << endl;
{
int B = 2, C = 3; //更内层块,新B、C,屏蔽外层B、全局C
cout << "A:" << A << ",B:" << B << ",C:" << C << endl;
}
return 0;
}
int A = 8, B = 7, C = 6;//全局变量(定义性声明)

全局 B 的作用域是整个程序;但是从第 7 行int B=1;开始,到 main 结束,全局 B不可见,被屏蔽
被屏蔽 ≠ 变量销毁!全局 B 还在内存里活着,只是直接写 B,编译器优先找离自己最近的内层名字
作用域解析符 :: 突破屏蔽
如果在内层块,我就想访问被屏蔽的全局变量,用 ::变量名
注意⭐重要限制:: 只能拿全局的,不能拿被屏蔽的局部自动变量。

🐯补充:static 外部静态变量的屏蔽
static int x;(文件作用域)会屏蔽别的 cpp 文件里面同名的全局变量; 同一个文件里面,不能同时写同名全局变量和 static 全局变量,编译报错。
2、extern引用声明的位置,决定可见域
很多人以为:全局变量只要定义了,在哪都能 extern 访问,✖ extern 写在哪,它的可见范围就受这个块限制

报错原因,extern 写在块内部:这个 extern 带来的可见域仅限这个大括号内部,出了},这个声明失效,外面不认识 A。 还有int A写在main()函数下面,准确地说,他写在了第9行代码的后面,导致找不到A
我们可以提前写上声明,如下图所示

总结 extern 要点:
extern 只是引用声明,不分配内存;全局变量只能有一处定义
extern 写在哪里,可见域就从哪里开始
extern 本身不自带跨文件全局可见性,每个文件要用这个全局变量,都要在本文件 extern 声明
同一个全局变量,可以写很多次 extern,可见域是所有 extern 覆盖范围的总和
3、重复定义和屏蔽的区别
✅ 屏蔽:不同作用域,同名,合法,就近匹配,不报错
❌ 重复定义:同一个作用域,同名,编译报错 redefinition
int a = 10;
int a = 20; // 同一个作用域,重复定义,报错!
void test(){
int a = 1;
{
int a = 2; // 不同作用域,屏蔽,合法,不报错
}
}
四、作用域和可见域
C++不允许函数里面嵌套定义函数,默认函数是全局作用域:
普通函数(不带 static):全局作用域,别的 cpp 文件可以 extern 声明之后调用
static 函数:文件作用域,仅限当前 cpp 文件使用,别的文件看不见、调用不了;同名 static 函数会屏蔽其他文件里同名外部函数
规则和全局变量几乎一模一样:
-
函数声明写在块内:只能这个块里面调用
-
函数声明写在文件头部:从声明行到文件末尾都可以调用
-
函数定义写在调用前面:不需要声明,直接可见
static int add(int, int);
static int add(int m, int n)
{
return m + n;
}
五、小结
生存期:内存存活时间,由存储类别决定(栈 / 静态 / 堆 new)
作用域:名字在源代码上理论生效的地盘,是文本范围,不是运行时内存
可见域:作用域的子集,屏蔽会切掉一部分可见区域;就近原则,内层优先
屏蔽 ≠ 销毁,外层变量还活着,只是直接写名字找不到;::只能解锁全局变量
extern 声明位置决定可见域,static 可以把变量 / 函数锁在当前 cpp 文件内
同作用域同名 = 重复定义报错;不同作用域同名 = 屏蔽
希望可以帮助到大家,感谢观看,如果觉得文章对你有帮助的话,请点赞收藏关注支持一下博主😍,如有不足请指点,我及时改正!👻一起加油!嘻嘻😜

网硕互联帮助中心




评论前必须登录!
注册