云计算百科
云计算领域专业知识百科平台

生存期、作用域、可见域,一文讲清!

前言:很多新手容易把这三个概念混在一起:生存期管「内存在不在」,作用域管「理论上代码哪块区域有权限访问这个名字」,可见域管「当前代码里,这个名字真正能找到它、合法访问」。可见域是作用域的子集

一、定义

形参调用时分配内存,调用结束释放,对应三条:生存期、作用域、可见域

通俗地讲,生存期指的是在程序运行过程中,变量从创建到撤销的一段时间。生存期的长短取决于前面所讲的存储方式,对于自动分配(栈分配),变量与其所在的代码块共存亡,对于静态分配(编译器预分配),变量与程序共存亡,程序开始执行时即已存在,一直到程序运行完毕退出后才被撤销,对于动态存储的内存块(不是指向该内存块的指针),由程序员决定其生存期。

名词通俗解释核心一句话由什么决定
生存期 变量在内存里存活的时间段:从分配内存 → 内存被回收销毁 内存有没有存在 存储方式(自动 / 静态 / 动态)
作用域 源代码文本区域,理论上这个标识符名字有效的范围(不考虑屏蔽) 名字理论上在哪块代码生效 声明位置、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 文件内

  • 同作用域同名 = 重复定义报错;不同作用域同名 = 屏蔽

  • 希望可以帮助到大家,感谢观看,如果觉得文章对你有帮助的话,请点赞收藏关注支持一下博主😍,如有不足请指点,我及时改正!👻一起加油!嘻嘻😜

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 生存期、作用域、可见域,一文讲清!
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!