生命周期和作用域
-
变量分类
- 局部变量 (声明在某个函数里)和 全局变量(声明在所有函数外面)
- 静态变量 和 非静态变量
-
变量的使用范围
- 作用域:可以使用这个变量的所有语句的总和
- 生命周期:某个变量能够使用的时间范围
-
非静态局部变量
- 作用域是包含函数里面的所有语句
- 生命周期是函数某一次执行的时间
- 类似于别墅里的家具(整个程序一个大型住宅区,里面若干个文件比作若干个小区,函数相当于某小区的一栋别墅),别的别墅里的人无法使用这个家具,而且只有这个别墅构建好了,那个家具才存在。别墅拆了,家具也没了。
-
静态局部变量
- 作用域包含函数里的所有语句
- 生命周期是整个程序的执行时间(函数执行前与结束后都可以使用)
- 类似于别墅下面的土地,只有这家别墅可以使用,但这些土地是一直存在的一直可以使用的,并不是有了别墅以后才有的,别墅拆了以后土地依然存在,依然可以使用。
-
非静态全局变量
- 作用域包含程序里的所有语句
- 生命周期是整个程序的执行时间
- 类似于小区外面的道路,道路是公共的,所有人都可以使用它。另外这些道路在没有这些小区的时候也是存在的,小区拆除了,道路依然存在,依然可以使用。
-
静态全局变量
- 作用域包含文件里的所有语句
- 生命周期是整个程序的执行时间
- 类似于小区里的路,小区之间是相互封闭的,别的小区不能使用这个小区的路,另外小区道路的存在也不受别墅的影响。
-
注意事项
- 全局变量和静态变量的初始化只在程序开始的时候执行一次(不管它们被写在什么地方)
- 全局变量和静态变量都会被自动初始化为0(如果没有明确初始化)
- 非静态局部变量如果没有初始化,那么它的内容是随机的
-
生命周期的意义在计算斐波那契数列效率上的体现
//用递归的思想来处理斐波那契数列
#include <stdio.h>
int fei(int sn) {
if (sn <= 1 ){
return 1; //分支处理递归函数不可处理的分解后的问题
}
//分解问题:要想算编号为 sn 的数字,需要算出编号为 sn-1 和 sn-2 的数字,然后相加
return fei(sn -2) + fei(sn -1); //假设递归函数已经写好,分别用参数sn-2 和 sn-1去调用fei
}
int main() {
int sn = 0;
printf("请输入编号: ");
scanf("%d", &sn);
printf("结果是%d\n", fei(sn));
return 0;
}
为了计算编号为40的数字需要把编号为38的数字计算两次,把编号为36的数字计算了3次,把编号为10的数字可能计算了上千遍。
下面来提高这个程序的运行速度,用一个数组来保存已经算过的数字,保证每个编号对应的数字只被算了一次
#include <stdio.h>
int fei(int sn) {
int arr[50] = {0};
if (sn <= 1 ){
return 1;
}
//先检查数组里有没有放那个编号对应的数字,这个分支去处理数组里还没有放好数字的情况
if (!arr[sn - 2]) {
arr[sn - 2] = fei(sn-2);
}
if (!arr[sn - 1]) {
arr[sn - 1] = fei(sn-1);
}
return arr[sn -2] + arr[sn -1];
}
int main() {
int sn = 0;
printf("请输入编号: ");
scanf("%d", &sn);
printf("结果是%d\n", fei(sn));
return 0;
}
然后发现时间更长了,因为 arr[50]是一个非静态局部变量,程序在运行时,内存里会有很多个数组一起存在,每个fei函数只能使用它自己的数组,因此这个fei函数是永远也不可能从这个数组得到别的fei函数以前计算好的数字。最后结果是不但以前做的事情一件都没有少做,还花费了些时间为这些数组准备存储区。
方法一: static int arr[50]; //数组不需要初始化,静态变量都会被自动初始化为0,静态局部变量的生命周期就是整个程序的执行时间,这就意味着所有的fei函数用的都是同一个数组。
方法二: int arr[50]; //做全局变量,生命周期也是整个程序的执行时间。