生命周期
- 变量的生命周期指可以使用变量存储位置的时间范围
- 变量的存储位置在程序运行期间不停地被创建又不停被回收
- 根据生命周期也可以把变量分为全局,局部和块
全局变量的生命周期是整个程序运行期间
局部变量的生命周期是函数调用期间
块变量的生命周期是块语句执行期间 函数的形参可以看成是局部变量,他们的生命周期也是函数执行期间
同一个函数同一个局部变量调用的存储位置是不一样的
#include <stdio.h>
void f1(void)
{
int num1;
num1++;
printf("num1是%d\n", num1);
}
void f2(void)
{
int num = 10;
int num1 = 11;
int num2 = num + num1;
f1();
}
int main(int argc, char **argv)
{
f1();
f2();
return 0;
}
这里f1()函数调用了两次,然后两次的结果是没有任何关系的。
递归函数的arr数组定义为全局变量,则整个函数都使用这一个变量,所以函数运行时间快。
重名变量
#include <stdio.h>
int num;
int main(int argc, char **argv)
{
printf("%d\n", num); //num = 0
int num = 1;
printf("%d\n", num); //num = 1
{
printf("%d\n", num); //num = 1
int num = 2;
printf("%d\n", num); //num = 2
}
printf("%d\n", num); //num = 1
return 0;
}
同名变量中优先使用块变量,其次为局部变量,最后为全局变量。
声明变量时的关键字
1、auto声明自动变量,一般不用
2、static关键字声明静态变量,静态变量的生命周期和作用域不匹配,一个大一个小。静态全局变量的生命周期不变,作用域是文件内部的所有语句,别的文件不能使用。静态局部变量作用域不变,生命周期为整个程序运行期间,
#include <stdio.h>
void f1(void)
{
static int num1;
num1++;
printf("num1是%d\n", num1);
}
void f2(void)
{
int num = 10;
int num1 = 11;
int num2 = num + num1;
f1();
}
int main(int argc, char **argv)
{
f1();
f2();
return 0;
执行结果为1,2。在加了static之后,两次调用f1的结果就有关系了,因为静态局部变量初始化默认为0。因为不管调用多少次,静态局部变量都是同一个存储位置,他的生命周期为整个程序运行期间。而且静态局部变量的初始化只是程序开始的时候执行一次。刚才的arr数组也可以用静态变量解决。
3、const关键字用来声明不可赋值的变量,只有在初始化时能够赋值。但是能够被改变。
4、volatile关键字也可以用来声明变量,其内容随时可能变化。好比你的工资卡有5000块钱交给您夫人了,但是第二天你去看的时候只有3000了,您夫人拿2000买了双鞋。。。。。。
为什么我们的存储位置不停地被收走,又不停的被分配?
程序运行时,文件内容读到内存中,被分成好几个段,每个段落为不同的存储数据格式,以及不同的数据使用方式。
程序运行时,文件内容读到内存中,被分成好几个段,每个段落为不同的存储数据格式,以及不同的数据使用方式。
代码段:用于保存语句转化成的数字,这个段落在运行时是不可以被修改。
全局段:包含所有全局变量和静态变量的存储位置,段落大小不会随着程序运行而改变
栈:包含所有局部变量,块变量,形式参数和返回值的存储位置,段落大小随着程序运行不断改变。
int main(int argc, char **argv)
{
f1();
f2();
return 0;
}
main函数的位置是固定不变的,而其他的函数都是向下移动的,栈为后进先出,最后运行的程序最先被回收,像垃圾桶一样。最后扔进去的垃圾最先倒出来,每次函数调用栈中占有专门的存储区域,当一个函数调用结束后它所占有的区域被计算机回收。
堆:包含所有动态分配的存储位置,堆中包含的存储位置可以看成是无限的,堆中存储位置的生命周期由程序猿管理,需要使用语句分配和回收
递归函数
什么是递归函数?
递归函数内部包含一条或多条函数调用语句,被调用函数就是它自己。
#include <stdio.h>
void print(int num)
{
if (num == 1)
{
printf("1\n");
return ;
}
printf("%d ", num);
print(num - 1);
}
int main(int argc, char **argv)
{
print(5);
return 0;
}
#include <stdio.h>
int sum(int num)
{
if (num == 1)
{
return ;
}
return num + sum(num - 1);
}
int main(int argc, char **argv)
{
int num = 0;
printf("请输入一个整数:");
scanf("%d", &num);
sum(num);
printf("%d到1的和为%d\n", num, sum(num));
return 0;
}
这是一个求和的递归函数。
递归函数如何执行?
下面用上面的求和函数聊一聊
这是一个求3到1的和的过程,主函数里面执行sum(3),然后sum(3)又要执行sum(2),sum(2)再执行sum(1),sum(1)执行自己,所以在那段相同的时间中,sum(1)永远在被执行,这段时间所有的调用都在进行。所以任何时候在真正工作的只有sum(1)。
下面再给出一个斐波那契数列的例子
/*1 2 3 5 8 13 21 34..........
1 2 3 4 5 6 7 8
给一个编号,说出斐波那契对应数列的值
*/
#include <stdio.h>
//int arr[50] = {};
int Fibonacci(int num)
{
int arr[50] = {};
if (num <= 2)
{
return 1;
}
if (!arr[num - 2])
{
arr[num - 2] = Fibonacci(num - 2);
}
if (!arr[num - 1])
{
arr[num - 1] = Fibonacci(num - 1);
}
return Fibonacci(num - 2) + Fibonacci(num - 1);
}
int main(int argc, char **argv)
{
int num = 0;
printf("请输入编号:");
scanf("%d", &num);
printf("编号为%d的数字是%d\n", Fibonacci(num));
return 0;
}
执行这段程序,当输入数字小的时候不会有什么感觉,但是我们输入大数字的时候,比如20,那程序运行的结果会很慢才出现。 但是我们将arr数组定义在函数外面,那结果就不一样,输入20的时候,结果很快就能出来。这是因为我们的数列在计算的时候每运行一次都要计算前面一个数的值,所以根据刚才的递归道理,每个调用都要调用前面的数值,导致运行时间很长,我们虽然用了数组去保存每个值,但是他只是在我们的函数里面,在整个函数来说是不起作用的,他还是要重新计算每次的值
这里就关系到一个局部变量,全局变量和块变量的问题了
全局变量的作用域包含程序中所有语句,未初始化的全局变量自动初始化为0。
局部变量声明在函数内部,作用域包括函数内部所有语句。
块变量是声明在函数内部的大括号中,块变量的作用域包含语句块中的所有语句
#include <stdio.h>
int num = 0; //全局变量
int main(int argc, char **argv)
{
int num1 = 0; //局部变量
printf("num1是%d\n", num1);
{
int num2 = 0; //块变量
printf("%d\n", num1); //合法,因为在声明在函数内部
}
}
printf("num2是%d\n", num2); //错,在块里面声明,作用域只在块里面