数组也可以作为形式参数使用
必须使用地址数据作为实际参数和数组形式参数配合使用
1#include<stdio.h>
2void print(int arr[5]){
3 int num=0;
4 for(num=0;num<=4;num++){
5 printf("%d",arr[num]);
6 }
7 printf("\n");
8 }
9int main(){
10 int arr[]={1,2,3,4,5};
11 print(arr);
12 return 0;
13 }
数组做形式参数时只是把形式参数写成数组的样子,真正的形式参数并不是数组,而是一个可以当做数组使用的变量
形式参数的存储区是被调用函数提供的,而数组不是真正的形式参数,所以数组形式参数里所包含的所有存储区都不是被调用函数提供的
数组做形式参数可以让被调用函数使用其他函数提供的存储区(变量不可以跨函数使用,而存储区可以)
数组形式参数可以实现双向数据传递,具有这种特征的形式参数叫输入输出参数
编写数组形式参数声明的时候可以省略数组里包含的存储区个数
1#include<stdio.h>
2void read(int arr[3],int size){
3 int num=0;
4 printf("请输入%d个数字:",size);
5 for(num=0;num<=size-1;num++)
6 scanf("%d",&arr[num]);
7 }
8int main(){
9 int arr[]={0,0,0},num=0;
10 read(arr,3);
11 for(num=0;num<=2;num++){
12 printf("%d ",arr[num]);
13 }
14 printf("\n");
15 return 0;
16 }
使用数组作形式参数的时候需要提供一个整数类型参数表示数组形式参数里包含存储区的个数
练习:编写函数生成一张彩票,在主函数里把彩票的数字都显示在屏幕上(可以重复)
1#include<stdio.h>
2#include<time.h>
3void read(int a[],int size){
4 int num=0;
5 srand(time(0));
6 for(num=0;num<=size-1;num++){
7 a[num]=rand()%36+1;
8 }
9 }
10int main(){
11 int arr[7]={0};
12 int num=0;
13 read(arr,7);
14 for(num=0;num<=6;num++){
15 printf("%d ",arr[num]);
16 }
17 printf("\n");
18 return 0;
19 }
不重复数字:
1#include<stdio.h>
2#include<time.h>
3void read(int a[],int size){
4 int num=0,cnt=0;
5 srand(time(0));
6 do{
7 a[cnt]=rand()%36+1;
8 for(num=0;num<=cnt-1;num++){
9 if(a[cnt]==a[num])
10 break;
11 }
12 if(num==cnt){
13 cnt++;
14 }
15 }while(cnt<size);
16 }
17int main(){
18 int arr[7]={0};
19 int num=0;
20 read(arr,7);
21 for(num=0;num<=6;num++){
22 printf("%d ",arr[num]);
23 }
24 printf("\n");
25 return 0;
26 }
C语言里函数形式参数的个数可以不固定,这种参数叫变长参数
变长参数不能事先命名,需要使用特殊的方法才能获得参数的数值
如果编译器首先遇到函数调用语句,就会猜测函数的类型格式,这个猜测结果叫做函数的隐式声明。隐式声明中包含一个整数类型存储区用来存放返回值,可以有任意多个不确定类型的形式参数。
隐式声明中形式参数的类型可以是整数类型或双精度浮点类型
如果隐式声明和函数的实际格式不一致,编译会出错
1#include<stdio.h>
2int main(){
3 int num=add(3,5);
4 printf("%d\n",num);
5 return 0;
6 }
7double add(double num,double num1){
8 return num+num1;
9 } 出错!!
函数大括号前面的部分叫函数声明。可以把函数声明作为一条语句单独写在文件开头,这条语句里的形式参数名称可以省略。
这种做法叫函数的显示声明
显示声明可以避免编译器采用隐式声明,所以,除了主函数以外的所有函数都应该显示声明
主函数不需要显示声明,因为主函数不能被调用
exit标准函数可以随时结束整个程序的执行(用return只能结束本函数的执行,使用exit可以执行本程序的执行)
为了使用这个标准函数,需要包含stdlib.h头文件,exit函数需要一个整数类型的实际参数,这个参数的作用和主函数的返回值一致。例:
1#include<stdio.h>
2#include<stdlib.h>
3void print(void){
4 printf("2\n");
5 // return 0;
6 exit(0);
7 printf("3\n");
8 }
9int main(){
10 printf("1\n");
11 print();
12 printf("4\n");
13 return 0;
14 }
C语言中函数可以调用自己,这种函数叫做递归函数
有些问题可以分解成几个小问题,至少一个小问题和原来的问题在本质上一样,只不过简单一点,具有这种特征的问题就适合采用递归函数解决
递归函数编写步骤:
第一步:用语句描述问题的分解方式 (假设递归函数已经完成)
第二步:在递归函数开头编写分支处理不可分解的情况。 (这个分支必须可以让函数结束,用return 结束)
用递归函数解决问题的思路叫递归
用循环解决同样问题的思路叫递推
练习:编写递归函数计算从1开始到某个给定正整数之间所有整数的和
首先:分解问题
第一步:前面num个数的和是由第一个到num-1的和加上num 。
return sum(num-1)+num;
第二步:写分支,大于1的每个数都是由1到它本身-1加上它本身组成,所以,1是不可分解的数 。
if(num==1){
return 1;
}
1#include<stdio.h>
2int sum(int num){
3 if(num==1){
4 return 1;
5 }
6 return sum(num-1)+num;
7 }
8int main(){
9 int num;
10 printf("请输入正整数:");
11 scanf("%d",&num);
12 sum(num);
13 printf("%d\n",sum(num));
14 return 0;
15 }
打印:123456..
1#include<stdio.h>
2void print(int num){
3 if(num==1){
4 printf("1\n");
5 return;
6 }
7 print(num-1);
8 printf("%d",num);
9 }
10int main(){
11 int num=0;
12 printf("请输入一个整数: ");
13 scanf("%d",&num);
14 print(num);
15 printf("\n");
16 return 0;
17 }
编写函数:每个数字是前面两个数字之和
例如 1 1 2 3 5 813 21 34
0 1 2 3 4 5 6 7 8
编写函数第一步:每一个数都是前一个数字加上前前一个数字:fei(num-2)+fei(num-1);
第二步:前两个数字不可分解 用分支:if(num<=1){ return 1; }
1 #include<stdio.h>
2 int fei(int num){
3 if(num<=1){
4 return 1;
5 }
6 return fei(num-2)+fei(num-1);
7 }
8 int main(){
9 int num=0;
10 printf("请输入一个编号: ");
11 scanf("%d",&num);
12 printf("计算结果是:%d\n",fei(num));
13 return 0;
14 }
能使用某个变量的语句叫做这个变量的作用域
声明在函数内部的变量叫做局部变量,局部变量的作用域包括函数内部的所有语句(作用域解释了为什么不能跨函数使用变量)
声明在函数外面的变量叫做全局变量,它的作用域包含程序中所有语句
没有初始化的全局变量自动被初始化成0
全局变量和局部变量可以重名,这种变量名优先代表局部变量
1#include<stdio.h>
2int num1; //全局变量
3void func(void){
4 int num=2;
5 int num1=10;
6 printf("num是%d\n",num);
7 printf("num1是%d\n",num1);
8 }
9int main(){
10 int num=1;
11 printf("num是%d\n",num);
12 printf("num1是%d\n",num1);
13 func();
14 return 0;
15 }
能使用局部变量解决问题就不要使用全局变量。
存储区的使用不受作用域的限制(可以跨函数使用存储区:例如数组做形式参数的时候)
存储区的使用收到生命周期的限制
生命周期是一段时间,只有在生命周期的时间范围内才可以使用存储区
全局变量的生命周期是整个程序执行的时间范围
局部变量的生命周期是函数一次执行的时间
如果函数多次运行,则每次函数运行时局部变量对应的存储区都不同(重新分配)
1 #include<stdio.h>
2 void func(void){
3 int num;
4 printf("num是%d\n",num);
5 num=10;
6 }
7 void func1(void){
8 int num=100;
9 func();
10 }
11 int main(){
12 func();
13 func1();
14 return 0;
15 }
执行结果:
tarena@tarena-virtual-machine:~/day09$./a.out
num是-1218764251 随机
num是134513662 随机
tarena@tarena-virtual-machine:~/day09$./a.out
num是-1218174427 随机
num是134513662 随机
声明变量时可以使用static关键字,这种变量叫静态变量
全局变量和局部变量都可以是静态的
静态变量生命周期一定是整个程序执行时间
如果一个函数多次执行,则每次静态局部变量对应的存储区都是同一个
如果一个函数结束了,则它的静态局部变量存储区还可以给别的函数使用
没有初始化的静态变量会自动被初始化成0
1 #include<stdio.h>
2 void func(void){
3 static int num;
4 printf("num是%d\n",num);
5 num=10;
6 }
7 void func1(void){
8 int num=100;
9 func();
10 }
11 int main(){
12 func();
13 func1();
14 return 0;
15 }
执行结果:
num是0
num是10
静态变量的初始化只在程序开始的时候执行一次
1 #include<stdio.h>
2 void func(void){
3 static int num=6;
4 printf("num是%d\n",num);
5 num=10;
6 }
7 void func1(void){
8 int num=100;
9 func();
10 }
11 int main(){
12 func();
13 func1();
14 return 0;
15 }
num是6
num是10
静态全局变量的作用域只包含声明它的那个文件里的所有语句
指针:
指针变量用来记录地址数据 (之前学的变量是用来记数或记字母的)
只有记录有效地址的指针才能使用
有效指针前使用*操作符可以找到另一个变量
无效指针是指没有指向地址的指针
int *p; 指针声明语句中的类型名称表示指针记录存储区地址的类型
1#include<stdio.h>
2int main(){
3 int num=0;
4 int *p_num; //指针变量声明
5 p_num=#
6 *p_num=10;
7 printf("num是%d\n",num);
8 return 0;
9 }
可以在一条语句中声明多个同类型的指针,这时必须在每个指针变量名称前单独写*
无效指针(没有捆绑的指针)分为两类:
1、 空指针里固定记录空地址(NULL),这个地址的数值就是0
2、 所有其它没有捆绑的指针都叫野指针
程序员应该保证程序中不会出现野指针,没有初始化的指针都是野指针,所以指针必须初始化
int *p_num ; 指针声明
p=&num1 ; 指针初始化
指针初始化的时候*没有参与初始化过程
练习:1 1 2 3 5 813 21 34
0 1 2 3 4 5 6 7 8
用递归算太慢,用数组:
1#include<stdio.h>
2int fei(int num){
3 static int arr[50]={0};
4 if(num<=1){
5 return 1;
6 }
7 if(arr[num-2]==0){
8 arr[num-2]=fei(num-2);
9 }
10 if(arr[num-1]==0){
11 arr[num-1]=fei(num-1);
12 }
13 return arr[num-2]+arr[num-1];
14 }
15int main(){
16 int num;
17 printf("请输入一个编号: ");
18 scanf("%d",&num);
19 printf("计算结果是:%d",fei(num));
20 }
分析:如果不用static 静态变量,则程序运算速度非常慢,因为每次递归运算都要重新计算
40
39 38
38 37 37 36
37 36 36 35 35 36 34 35
用了静态变量后,每次运行就把相应的数字记录在数组里,下次直接调用
另一种方法也可以把arr[50]放程序开头,当做全局变量使用