[C++系列] C语言基础 day09 函数,递归与作用域
in C/C++ with 0 comment

[C++系列] C语言基础 day09 函数,递归与作用域

in C/C++ with 0 comment

#函数的形式参数

数组是可以作为形式参数使用的。
数组做形式参数时真正的形式参数并不是数组,而是一个可以当作数组使用的变量。
数组形式参数里包含的所有存储区都不是吧被调用函数提供的。
声明数组形式参数的时候可以省略中括号里的数数字。
使用数组形式参数的时候需要另外提供一个整数类型的形式参数,这个参数表示数组形式参数里包含的存储区个数。
使用数组形式参数可以让被调用函数使用其它函数的存储区。
数组形式参数可以实现双向数据传递,具有这种特征的参数叫做输入输入参数。

#练习

编写函数把一个数组里的所有存储区内容变成相反数。

#include<stdio.h>
void reve(int arr[],int size){
for(int i=0;i<=size-1;i++){
arr[i]=0-arr[i];
}
}
int main(){
int arr[]={1,2,3,4,5,};
reve(arr,5);
for(int i=0;i<=4;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}

C语言里的函数参数的个数可以不固定。
这种参数叫做变长参数。
变长参数不能在编写函数的时候命名,
在被调用函数中通过特殊的方法才能得到变长的参数内容。
如果编译器首先遇到函数调用语句就猜测函数的格式。计算机认为函数有一个整数类型存储区用来记录返回值,函数可以提供任意多个不确定类型的形式参数。
这个猜测结果叫做函数的隐式声明。
函数参数隐式声明里参数的类型只能是int或double
如果函数的隐式声明和函数本身的格式不一致,则编译就会出错。
函数大括号前面的部分可以单独写成一条语句,这种语句叫做函数声明语句。
函数声明语句里可以省略形式参数的名称。
把函数声明语句写在文件开头叫做函数的显式声明。
显式声明可以避免隐式声明。
除了主函数以外的所有函数都应该进行显式声明。

exit标准函数可以立刻结束程序的执行。
为了使用这个标准函数需要包含stdlib.h头文件。
使用这个函数的时候需要一个整数类型的实际参数,这个函数参数的作用和主函数返回值的作用一致。

#递归

在C语言中,允许函数调用自己
能调用自己的函数叫递归函数。

如果一个问题可以拆分成多个小问题,至少一个小问题和原来的问题在本质上一样,但是稍微简单一些。具有这种特征的问题就可以采用递归函数解决。
递归函数编写步骤:
1,编写语句解决分解后的每个小问题(这个时候要假设递归函数已经可以使用了。)
2,在递归函数的开头编写分支处理不可分解的情况。(这个分支必须保证函数可以结束)

#练习

#把从1开始到给定的数字从小到大显示在屏幕上。

#include<stdio.h>
void print(int max){
if(max == 1){
printf("1 ");
return;
}
print(max-1);
printf("%d ",max);
}
int main(){
print(10);
printf(" ");
return 0;
}

采用递归函数解决问题的思路叫做递归
采用循环解决问题的思路叫做递推。

检验递归函数的时候首先用最简单的参数进行测试,然后逐渐把参数变得复杂,然后呢继续测试。如果所有参数测试结果都能成功就说明递归函数编写正确。

#练习

#编写递归函数计算从1开始到某个正整数之间的所有整数的和并把结果传递给调用函数。

//递归函数练习
#include<stdio.h>
int sum(int max){
if(max==1){
   return 1;
}
return sum(max -1)+max;
}
int main(){
printf("%d",sum(10));
}

#练习

有一组数:
1 1 2 3 5 8 13 21 。。。
0 1 2 3 4 5 6 7.。。。
#根据编号利用递归函数计算出数字。

//递归函数练习
#include<stdio.h>
int fei(int num){
if(num <= 1){
return 1;
}
return fei(num-2)+fei(num-1);
}
int main(){
int num = 0;
printf("输入编号:");
scanf("%d",&num);
printf("结果是 %d\n",fei(num));
return 0;
}

#练习

在上一个练习中的代码中,由于进行递归时有的数字需要计算多次,优化后的代码如下

//递归函数练习
#include<stdio.h>
int fei(int num){
int arr[50]={0};//编号为几的数字放在编号为几的数字。
if(num <= 1){
return 1;
}
if(!arr[num-2]){
 arr[num-2]=fei(num -2);
}
if(!arr[num-1]){
arr[num-1]=fei(num-1);
}
return arr[num-2]+arr[num-1];
}
int main(){
int num = 0;
printf("输入编号:");
scanf("%d",&num);
printf("结果是 %d\n",fei(num));
return 0;
}

但是这样变的更慢了,用三种方式优化这组代码。

#变量的作用域

C语言里把能使用某个变量的所有语句叫做这个变量的作用域。
声明在函数中的变量叫做局部变量,它的作用域只包含函数里面的所有语句。
声明在所有函数外边的变量叫做全局变量,全局变量的作用域包含程序里的所有语句。
没有初始化的全局变量自动被初始化成0
全局变量可以和局部变量重名,这种变量名优先代表局部变量。
如果全局变量和局部变量都能解决问题,就应该优先采用局部变量。

#存储区生命周期

存储区的使用不受作用域限制,可以跨函数使用存储区。
存储区的使用受生命周期的限制。
生命周期指一段时间,在生命周期开始的时候,计算机把存储区分配给程序使用,在生命周期结束的时候计算机把存储区收回。
全局变量的生命周期是整个程序的执行时间
局部变量的生命周期是函数某一次执行的时间范围。
在函数开始执行的时候计算机为局部变量分配存储区,在函数结束的时候计算机把局部变量的存储区收回。
如果函数多次执行则每次执行时局部变量对应的存储区都可能不同。

#静态变量

静态变量的生命周期和作用域跟普通变量不一样。
声明静态变量需要使用static关键字。
全局变量和局部变量都可以声明为静态的。
静态局部变量的作用域还是局部变量的作用域。

静态变量的生命周期是整个程序的执行时间。
没有初始化的静态变量自动被初始化为0,
静态变量的初始化只在程序开始的时候执行一次。
静态全局变量的作用域之包含声明它的那个问价里的语句,
不可以跨文件使用静态全局变量。

变量的作用域分为三种情况:局部变量,静态全局变量,非静态全局变量
变量的生命周期分为两种情况:非静态局部变量,和其它所有。

#练习

编写递归函数计算两个数字的最大公约数

如:
54 66
12 54
6 12
#

#include<stdio.h>
int yue(int num,int num1){
int tmp =0;
tmp = num;
num = num1 % num;
num1 = tmp;
printf("num : %d ,,, num1 : %d\n",num,num1);
if(num1 % num == 0){
return num;
}
return yue(num,num1);

}
int main(){
printf("虽大公约数是 %d \n",yue(54,66));
return 0;
}
//简洁版
#include<stdio.h>
int func(int num,int num1){
if(!(num1 % num)){
return num;
}
return func(num1 % num,num);
}
int main(){
printf("虽大公约数是 %d \n",func(54,66));
return 0;
}

预习

Responses