当前位置: 首页 > news >正文

变量的作用域和生命周期

大家好我是清墨,欢迎收看本期文章。

1. 数据存储的 3 要素

计算机程序在存储数据时必须跟踪 3 个基本属性:

  1. 信息存储在何处,在什么地方定义变量,决定了信息存储在何处
  2. 存储的值是多少,值是多少是通过赋值语句(或者初始化语句)决定的
  3. 存储的信息是什么类型,定义变量的时候会提供数据类型

2. 全局变量和局部变量

我们先看下面的程序例子

#include<bits/stdc++.h>
using namespace std;
int a; // 在 函数以外的地方定义的变量就是全局变量
int func(int t)
{int b=t*10+a;  // 在函数内定义的就是局部变量return b;
}
int main(){int c;  //在 main 函数内定义的变量是局部变量cin >>c>>a;cout << func(c);for(int i=1;i<=10;i++) // 循环变量 i 是局部变量,离开了 for 语句的范围{bool ok=false; // 局部变量 ok ,它的声明周期仅仅限于 本轮 for 循环。if(i%2==0) ok = true;if(ok) cout<<i<<endl;}return 0;
}

 

上面程序中有 5 个变量,都是 int 类型,名字分别是 a, b, c, i, ok 。 这 5 个变量的作用域和生命周期各不相同。

作用域指的是这个变量能被访问到的范围,在这个范围以内就能访问到,超出了这个范围就访问不到,就好像这个变量不存在一样。

生命周期讲的是这个变量什么时候被创建,什么时候被销毁。就好像人会有生老病死一样,变量也一样有。当变量被销毁之后,它就不复存在,你再都不能把它找回来了。这和作用域不太一样的,一个变量可以还存在,只是你在作用域之外访问不到它而已,就好比你和你的朋友被各自隔离在两个海岛上,没电话,没通信,你和你的朋友都是这个世界的客观存在,但是你没办法和你的朋友沟通。但是如果变量已经被销毁,那么在任何地方都访问不到了,那就不仅仅是隔离的问题,是压根就没有这个东西。

  1. a 是全局变量。这意味着在整个程序的任何一个地方都可以访问到全局变量 a;除非是在某个局部范围内,你又另外定义了一个局部变量也叫 a ,那么在这个局部范围内你引用的 a 就是局部变量的 a,也就是说同名的时候,局部变量会屏蔽全局变量。全局变量都是在程序开始运行的时候就创建,到程序结束的时候被销毁,你的程序有多长的生命,全局变量就有多长的生命。

  2. c、b、i、ok 都是局部变量。c 是在 main 函数内定义的,所以,在 main 函数范围内,都可以引用(访问)c; b 是 func 函数内定义的,所以,在整个 func 范围内,都能访问 b 。上面的内容都比较好理解,但是,大家还需要理解反过来的一层意思局部变量值能在定义它的那个小范围(作用域)内被访问,离开了这个作用域,这个变量就没办法访问到,比方说,如果你在 main 函数里试图访问 func 函数里的 b ,那是要出错的,在 func 里面想访问 main 函数里面的 c 也是会出错的。

  3. i 和 ok 同样是局部变量,但是它们 2 个更特殊一些。i 是在 for 语句的圆括号里定义的,那么 i 的生命周期仅仅限于 for 语句之内,这个 for 语句结束了之后,i 就被销毁了,再都没有 i 了。即便你后面再写一个 for,继续用 i 来做循环变量,那个 i 也是另外一个 i 了。就好比你家有一条宠物狗叫 Boby,它死了,然后你又买了一条新的狗,你为了怀念以前的狗,给新的狗也起了个名字叫 Boby,但是此 Boby 非彼 Boby。

  4. ok 变量是在 for 的循环体里面定义的,那么它的作用域就仅限于 for 内部,而且,它的生命周期是本轮循环。也就是说,每循环一遍,这个程序就定义一次 ok,本轮循环结束,就销毁 ok,所以循环了 10 次,就先后定义了 10 个 ok ,也销毁了 10 次 ok,每一个 ok 都是一个不一样的 ok,只不过名字相同而已。如果你的程序要循环非常多次的,那就一定要避免这种变量定义方式,因为变量的定义和销毁是会产生开销的,这个开销要比做加减乘除大得多,如果循环 107107 次,执行 107107 次变量定义和销毁,会花很多时间。

3. 合理利用全局变量和局部变量的特性

可能会有同学提出,既然全局变量能在整个程序范围都被访问,那我把变量都定义成全局变量,那岂不是更好?这个问题非常复杂,因为评判这样做的好坏是有很多个角度的。

如果单单从参数传递的角度来看,把变量定义成全局变量,那就不需要传参,所以会减少模块之间的接口复杂性。请看下面的例子

求最大公约数,版本 1

#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{int c;while(a%b!=0){c = a % b;a = b;b = c;}return b;
}
int main(){int a,b;cin>>a>>b;cout<<gcd(a,b);
}

 

求最大公约数,版本 2

#include<bits/stdc++.h>
using namespace std;
int a,b,c;
void gcd()
{while(a%b!=0){c = a % b;a = b;b = c;}c = b;
}
int main(){cin>>a>>b;gcd();cout<<c;
}

 

上述两个版本,要做的事情都类似,第一个版本中,main 函数内调用 gcd ,通过 参数 把要两个数传给 gcd 函数。gcd 函数算完之后通过返回值把结果送回给 main。

第二个版本,a, b 都是全局变量,计算结果放在 c,c 也是全局变量。所以,main 函数和 gcd 函数之间不需要传递和会送任何的数据,换一句话说,这两个部分之间的相互配合是很简单的。但是,简单未必是好事。如果这个程序要计算的东西很复杂,我们算最大公约数的时候不希望破坏原来的 a 和 b,它们后面还有别的地方要用到,改了就反而不行的,那么第一个版本显然就是更合理的。

在工作中,用于生产、商业运作的程序往往都是很严肃的程序,程序的可靠性、代码的可管理性都是很重要的考量因素,如果代码简单了但是牺牲了程序的可靠性、稳健性、可管理性,那么这样换回来的简单是不会被采纳的。

但是,在比赛的时候,程序往往就是一个简单的问题,解题有时间限制,写一个非常漂亮、规范的程序并不给你加分,只要你能掌控得住,这个程序或许不是那么规范,节省了时间可能会给你带来分数上的帮助。所以的的确确很多时候会依赖全局变量来简化程序的模块接口。我们看一些牛人写的程序,你可能会挑剔他有各种毛病,并不是那个牛人不知道这些问题,而是而是他们掌控得住你说的那些问题。

再讲大一点,ACM 的比赛和 OI 的比赛因为规则不一样,代码风格都不太一样。ACM 是比时间的,解题速度和最终的分数关系非常大,所以,ACM 会有一些自己的代码风格,能节省 10 秒就是 10 秒。我们过去一直是基于 OI 的比赛规则来训练的,过去的区赛也是 OI 模式。这种模式下,对解题时间没有考量。因此我们会更加注意把代码写好一点,严谨一点。

这个世界很大,非常复杂,当你知道的东西越多,你越是会觉得有趣。


http://www.mrgr.cn/news/31374.html

相关文章:

  • React基础教程(10):React Hooks
  • 建筑工程五方责任主体项目负责人质量终身责任追究暂行办法
  • 哈希表的使用
  • Spring Boot中使用注解拦截器实现通用校验器和基于角色的权限注解
  • java之杨辉三角问题
  • A Simple Encoder-Decoder for Open-Vocabulary Semantic Segmentation
  • safepoint是什么?有什么用?
  • anaconda的windows新手安装及配置教程(适用于物联网工程、计算机专业)
  • Matlab R2024B软件安装教程
  • web基础—dvwa靶场(十)XSS
  • 极越联手百度这你受得了吗!SU7还能稳坐“7字辈”头把交椅?
  • 深入探索:深度优先遍历与广度优先遍历的奥秘与应用
  • OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【内核通信机制】下
  • 基于正点原子Linux开发板的智能监控与家电控制系统设计:深度解析Video4Linux和TCP/IP技术栈
  • OpenAI GPT o1技术报告阅读(4)- 填字游戏推理
  • 正点原子阿尔法ARM开发板-IMX6ULL(八)——串口通信(寄存器解释)(补:有源蜂鸣器)
  • POS共识机制简介
  • Viper学习与使用
  • 芹菜麦饭的做法
  • Java流程控制语句——条件控制语句详解(附有流程图)#Java条件控制语句有哪些?#if-else、switch