0.问题引入
有时候,经常需要在一个程序中,对一个数组进行输入输出
如:
int a[3][4];
int i,j;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
scanf("%d",&a[i][j]);
}
}
for(i = 0;i<M;i++)
{
for(j=0;j<N;j++)
{
printf("%d\n",a[i][j]);
}
}
int b[4][5]
....
上面的两个代码块,功能是相同,只有数组名,和元素个数
代码形式也是一样的。对于同一个功能而不同参数的代码块,可以设计
功能函数(如:用来对二维数组进行赋值)。我们将这个代码块,写成
一个函数之后,就可以被我们重复使用。
这种重复使用的相同的代码块 ===> 函数
1.函数
什么是函数?
function /函数、功能
函数是完成某种功能的指令序列的封装
C语言指令(c语言语句)必须写在函数内。
函数可以实现代码的重复利用,以及实现模块化设计
2.如何来设计一个函数
函数是用来实现一个功能或者任务的
实现任务,可能需要有多少资源(输入)
实现任务,结果
实现任务,思路,步骤
四点:
(1)明确函数的功能是什么?完成什么任务,
确定函数的目标
如:
求两个整数之和
定一个“函数名”:C语言标识符的规定
sum:用来求两个整数之和
一但确定“函数名”,这个名字就代表这个特定的功能。
(2)完成该目标需要的已知条件,输入参数
如:
求两个整数之和
想要求两个整数的和,那我必须要知道是哪两个整数!!!
int a, int b
(3)完成该目标的输出结果 => 返回值
返回值的类型 就是输出结果的类型
如:
求两个整数之和
最终想要将这两个整数的和,进行反馈
typeof(a+b) => int
(4)完成目标,具体步骤(代码实现)
如:
求两个整数之和
int s;
s = a+b;
return s;
3.C语言中函数的语法形式
语法;
返回值的类型 函数名(输入参数列表)
{
//代码实现
}
返回值的类型:
void:表示该函数没有返回值,void不可以省略的!!!
函数名:
对象的标识符,代表着这个对象
符合c语言的规定,尽量做到”见其名,知其意“
输入参数列表:
(类型1 参数1,类型2 参数2,类型n,参数n )
return:
返回的意思。
return 只能用在函数内部,表示函数结束的意思。
return; //函数结束,没有返回值
return 表达式; //函数结束,带一个返回值,“表达式的值”
函数的返回值类型,就是return后面语句的“表达式的值”的类型
例子:求两个整数之和
sum: 用来求两个整数之和的
a : 加数 a
b : 加数 b
返回值:
返回a+b的值
int sum(int a,int b)
{
int s;
s= a+b;
return s;
}
练习:
写一个函数,求从集合[i,j]的正整数之和。
int sum_i_j(int i,int j)
{
int x;
int sum =0;
for(x=i;x<=j;x++)
{
sum +=x;
}
return sum;
}
是不是函数写完了,那么它就会自动调用执行嘛?
不会!!!
4.函数的调用过程
函数调用: 调用一个已经写好的函数去执行
(1)如何去调用一个函数
语法:
函数名(实参列表)
a,需要指定是哪个函数/任务
指定该函数名/任务
b,指定该任务需要的参数(实际参数)
例子:
int sum(int a,int b)
{
int s;
s= a+b;
return s;
}
函数名 => sum
实际参数:3,4
调用: sum(3,4)
形式参数: 定义函数时的参数 => “形参”
实际参数: 调用函数时传入的参数 => “实参”
在调用函数的时候,指定实参,是不需要指定类型的。
在调用函数的时候, 需要指定“实参”,要和形参相对应
sum(int a,int b)
int x ,y;
sum (x,y); //ok
sum(x+y,5+6); //ok
x+y ==> a
5+6 ==> b
例子:
int m = 3,n = 5;
sum(m,n); //ok
sum(3+5,5*7); //ok
sum(m*n); //ERROR 实参与形参的个数不对应
sum(1.0*m,n); //ERROR 实参与i形参的类型不对应
sum(int m,int n);//ERROR 函数的调用,实参不需要类型
sum(sum(5,6),7);// ===>
sum(5,6) ==> a
7 === > b
(2)函数调用表达式
函数名(实参列表) ==> 函数调用表达式
sum(5,6) => 函数调用表达式
函数调用表达式的值,就是去执行这个函数时,函数的返回值
(return 语句后面的表达式的值)
问题:没有返回值的函数,函数调用表达式难道就没有值了嘛?
void func(int a,int b)
{
.....
return 0 ; // ERROR 此函数没有返回值
return ;
}
有,但是是未知的!!!
练习:
1.写一个函数,将三个整数从大到小打印。
Print: 将三个整数从大到小打印
参数: x,y,z
返回值: 无
void Print(int x,int y,int z) --->函数头
{
int temp;
//1.先保证x>y
if(x<y)
{
temp = x;
x = y ;
y = temp;
}
// 2.再保证 x>z
..
// 3.最后保证y>z
...
printf("%d > %d > %d\n",x,y,z);
}
(3)函数声明
就是告诉编译器这个对象的名字,是一个函数,
会在后面的某一个地方去定义,实现。
语法:
函数头;
int sum(int,int);
or
返回值类型 函数名(参数类型列表);
5.数组作为函数的参数
当一个函数的参数是一个数组时,
函数的声明时,数组当形式参数,该如何去描述数组呢?
数组作为形式参数:
数组元素的类型 数组名[] , int 数组元素的个数
例子:
一维数组:
int a[10]; //把数组a传入一个函数xxx
//参数b 接收一个数组元素为int类型的数组 的数组名
int b[]
// 参数n 接收数组元素的个数
int n
===> void xxx(int b[],int n)
{
//b[0] ->a[0]
//b[1] ->a[1]
.....
}
调用:
函数名(实参列表);
xxx(a,10);
二维数组:
int a[3][4]; //把数组a传入一个函数 yyy
// int [4] a[3]
typeof(a[0]) => int [4]
数组元素的类型 数组名[] ,int 数组元素的个数
参数b 接收一个元素类型为int[4]类型的数组名
参数n 接收数组元素的个数
void yyy(int [4]b[],int n)
===>void yyy(int b[][4],int n)
{
}
矩阵角度:
元素类型 数组名[][列数],int 行数
例子:
char ch[3][4]
void zzz(char c[][4],int n)
{}
练习:
1.定义一个一维数组,然后要写一个函数,实现对一维数组赋值的功能
以及在写一个函数实现打印一维数组各个元素值的函数。
#include <stdio.h>
void arr_input(int b[],int n);
void arr_output(int a[],int n);
int main()
{
int a[10];
int b[5];
arr_input(a,10);
arr_output(a,10);
//arr_input(b,10);
//arr_output(b,10);
}
void arr_input(int b[],int n)
{
int i;
for(i=0;i<n;i++)
{
scanf("%d",&b[i]);
}
}
void arr_output(int a[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%d\n",a[i]);
}
printf("\n");
}
2.写一个函数,实现对二维数组的每一个元素赋值,再写一个函数,
用来打印二维数组中每一个元素的值。
#include <stdio.h>
#define N 4
#define M 3
void arr2_input(int a[][N],int n);
void arr2_output(int a[][N],int n);
int main()
{
int a[M][N];
arr2_input(a,M);
arr2_output(a,M);
}
6.再论函数声明的问题
“声明”:
C语言中声明是一个已经存在东西。
声明这个标识符是个什么东东 (告诉编译器,这个标识符是啥)
为什么要声明?
C语言编译文件时,是第一行到最后一行,一行一行进行编译的
而且在多个文件中,是一个文件一个文件的编译,
有时候,在一个文件1.c用到2.c定义的对象(变量,函数...)
在编译1.c的时候,遇到这个对象名,C语言编译器它就不知道这个标识符
是个什么玩意....
即使在同一个文件中,在前面遇到某个对象的名字,而这个对象是在本文件的后面
定义的,此时c语言编译器他也不知道这个标识符是个什么玩意
so,一般来说,在源文件的前面,要进行标识符的声明。
变量声明:
局部变量:
定义在函数{}内的变量。
它仅在定义它本身的那个{}内生效
全局变量:
是定义在函数{}外的变量。
它在整个工程都生效。
外部变量:
指定义在本文件之外的全局变量。
在使用外部变量之前,需要进行外部声明。
extern 变量的类型 变量名
类型声明:
声明一个新的类型名:
typedef 旧类型 新类型名;
如:
typedef unsigned int uint32_t;
unsigned int a
===>
uint32_t a;
数组声明:
全局数组
局部数组
函数声明:
本文件的函数的声明:
函数头;
外部的函数的声明:
extern 函数头;
约定:
声明语句一般在使用的前面
练习:
1.写一个函数,判断一个一维数组是否有序,
如果有序,是升序还是降序!!!
int Is_Sort(int a[],int n)
{
/*
定义两个变量 t1,t2,分别来记录‘>’,'<'的个数
遍历完整个数组,再判断 t1, t2 的值
*/
int t1,t2;
t1 = t2 = 0;
int i;
for(i=0;i<n-1;i++)
{
if(a[i]<a[i+1])
{
t2++;
}
else
{
t1++;
}
}
if(t1==n-1)
{
return 1;
}
else if(t2 == n-1)
{
return -1;
}
else
{
return 0;
}
}
2.写一个函数,求一个二维数组是否对称
int a[M][N];
思路:
if(水平对称||垂直对称)
{
return 1;
}
else
{
return 0;
}
垂直对称;
/*
chuizhi: 判断一个二维数组是否关于垂直线对称
@a : 数组名
@n : 行数
返回值:
对称 ---1
不对称 ---0
*/
int chuizhi(int a[][N],int m)
{
int i,j;
for(i=0;i<m;i++)
{
for(j=0;j<N/2;j++)
{
if(a[i][j]!=a[i][N-j-1])
{
return 0;
}
}
}
return 1;
}
水平对称.....
3.写一个函数,打印n行的杨辉三角
1
11
121
1331
14641
15101051
=> a[i][j] = a[i-1][j-1] + a[i-1][j]
void YangHui(int n)
{
int i,j;
for(i=0;i<n;i++)
{
for(j=0;j<=i;j++)
{
if(j==0)
{
a[i][j] = 1;
}
else if (i==j)
{
a[i][j] = 1;
}
else
{
a[i][j] = a[i-1][j-1] + a[i-1][j];
}
printf("%d",a[i][j]);
}
printf("\n");
}
}
4.求水洼数。(选做)
小明家前面有个坪,但是呢这个坪它不平,下雨后,就会有水洼
1 -- 表示有水
0 -- 表示没水
一个点的水,会和周围的其他的点的水连成一片大水洼
输入: M*N
1110010
0110110
0000101
1000001
0010100
思路;
遍历数组
(1)遇到1,count++
(2)把这个点,以及它周围的周围.....点全部清0
5.求按照Hanio的规则,把n个盘子从A柱移动到C柱,中间可以利用B柱,需要多少步?
6.写一个函数,求一个整数的二进制形式中有多少个1?
7.写一个函数求两个数的最大公约数。
8.写一个函数计算x的n次方
9.写一个函数将输入的一个整数反序打印出来,例如输入1234,则输出4321,输入-1234,则输出-4321
7.递归函数(难点)
例子:
假设第一个人的年龄是10岁,每后面一个人,他的年龄要比他前面那一个人的年龄
要大两岁,写一个函数来求第n个人的年龄
/*
Get_Age : 用来求第n个人的年龄
@n : 第n个人
返回值: 返回第n个人的年龄
*/
int Get_Age(int n)
{
/*
Get_Age(5) => Get_Age(4)+2
=>(Get_Age(3)+2)+2
((Get_Age(2)+2)+2)+2
(((Get_Age(1)+2)+2)+2)+2
=>18
if n>1 Get_Age(n) =>Get_Age(n-1)+2
if n = 1 Get_Age(1) = 10
*/
if(n>1)
{
return Get_Age(n-1)+2;
}
else if(n=1)
{
return 10;
}
}
栈空间!!!
”自己调用自己“
递归函数:
直接或间接调用函数本身
什么情况下用递归?
像上面的那一个例子:
要求第n个人的年龄
<---- 第n-1个人的年龄 +2
< --- 第n-2个人的年龄+2 +2
解决一个问题时候,解决思路化成与问题本身类型的问题的时候。
“递归”!!!
是不是所有的递归问题,c语言都支持呢?
不是的
一定要满足两个条件:
(1)问题本身就是一个递归问题
(2)递归不能是无限递归
递归是需要有一个跳出递归调用的条件
递归到一定程度,答案是显然易见的
练习:
1.写一个函数,来求n!(递归)
#include <stdio.h>
int Jiecheng(int n);
int main()
{
int n;
scanf("%d",&n);
int m = Jiecheng(n);
printf("%d\n",m);
}
int Jiecheng(int n)
{
if(n>1)
{
return Jiecheng(n-1)*n;
}
if(n==1||n==0)
{
return 1;
}
}
2.用递归的办法,求一个一维数组之和。
int sum_arr(int a[],int n )
{
if(n==1)
{
return a[n-1];
}
return sum_arr(a,n-1) + a[n-1];
}
3.用递归来求斐波拉契数列的前n项和。
a[i] = a[i-1]+a[i-2];
1 1 2 3 5 8 13
/*
value_n: 求斐波拉契数列的第n项值
@n : 第n项
返回值:
第n项的值
*/
int value_n(int n)
{
if(n==1||n==2)
{
return 1;
}
return value_n(n-1)+value_n(n-2);
// n=6 value_n(5) + value_n(4);
value_n(4) +value_n(3)
}
sum = 0;
for(i=1;i<=n;i++)
{
sum += value_n(i);
}
4.用递归的方法来判断一个一维数组是否递增。
/*
Is_Dizeng : 判断一个一维数组是否递增
@a : 数组名
@n :数组元素个数
返回:
1.递增
0.不递增
*/
int Is_Dizeng(int a[],int n)
{
if(n==1)
{
return 1;
}
return Is_Dizeng(a,n-1)&&a[n-1]>a[n-2];
}
5.汉诺塔的问题(拓展题)
按照汉诺塔的规则,把n个盘子 从A柱,移动到C柱,
中间可以利用B柱,需要把移动的步骤打印出来
void Hanio(int n,char x,char y,char z)
a,确定函数名
b,确定参数
n A B C
n:有多少个盘子
x :从哪里开始
y: 中间可以利用的那一根
z: 转到哪里去 终点
{
S1: 按照汉诺塔的规则,把n-1个盘子,从A挪动到B柱,中间可以利用C柱,将其步骤输出。
Hanio(n-1,A,C,B)
S2: 讲第n个盘子,从A柱移动到C柱
printf("%c->%c\n",A,C);
S3:把n-1个盘子,从B挪动到C,中间可以利用A柱。
Hanio(n-1,B,A,C);
if(n==1)
{
A---->C
}
}
回顾:
1.函数
2.设计一个函数
a,确定函数名
b,指定函数的参数
c,确定返回值的类型
d,算法,代码具体实现
3.定义函数
语法:
返回值的类型 函数名(形参列表)
{
// 语句,指令
}
return :
return 表达式
4.函数调用
4.1如何调用一个函数
函数名(实参列表)
实参跟形参一一对应(数目,类型),实参是不需要类型的。
4.2函数调用表达式
函数名(实参列表)
函数调用表达式的值,就是函数中,return后面的那个表达式的值
4.3函数的声明
函数头;
int sum(int a,int b);
int sum (int ,int );
5.数组作为函数的参数
一维数组
数组元素类型 数组名[] , int 数组元素个数
例子:
int a[10];
===> xxx(int b[],int n)
二维数组
数组元素类型 数组名[], int 数组元素个数
例子:
char ch[3][4]
===>xxx(char c[][4],int n);
矩阵:
二维数组元素的类型 数组名[][列数] ,int 行数
6.对象声明
变量声明:
局部变量
外部变量
全局变量
类型声明:
typedef 旧类型 新类型名;
数组声明:
局部数组
全局数组
函数声明:
本文件中的函数声明
外部的函数声明
7.递归函数
”自己调用自己“
递归的思路,只是我们解决某些问题的一种方式。
N -> N-1 >......
递归条件:
a,你要解决的问题,必须是一个递归问题
b,必须要有结束“无限递归”的条件。
练习
1,如果函数调用时,用数组名作为函数的参数,以下描述中正确的是(A)
A,实参与其对应的形参共同使用同一段存储空间
B,参数与其对应的形参占用相同的存储空间
C,实参将其地址传递给形参,同时形参也会将该地址传递给实参
D,实参将其地址传递给形参,等同实现了参数之间值的双向值传递
2,若定义函数float *fun(),则函数fun的返回值为(B)
A,一个实数
B,一个指向实型变量的指针
C,一个指向实型函数的指针
D,一个实型函数的入口地址
3,C语言规定,程序中各个函数之间(A)
A,即允许直接递归调用也允许间接递归调用
B,不允许直接递归调用也不允许间接递归调用
C,即允许直接递归调用不允许间接递归调用
D,不允许直接递归调用允许间接递归调用
4,若程序中定义函数
float myadd(float a,float b)
{return a+b;}
并将其放在调用语句之后,则在调用函数之前应对函数进行说明,以下说明中错误的是(A)
A float myadd(float a,b)
B float myadd(float b,float a)
C float myadd(float,float)
D float myadd(float a,float b)
5,下面程序段运行后的输出结果是(a=3,b=5)
(假如程序运行时候输入5,3回车)
int a,b;
void swap()
{
int t;
t=a;a=b;b=t;
}
main()
{
scanf("%d,%d",&a,&b);
swap();
printf("a=%d,b=%d\n".a.b);
}
6,以下程序的正确运行结果是(8,17)
#include <stdio.h>
int func(int a,int b)
{
static int m=0,i=2;
i+=m+1;
m=i+a+b;
return m;
}
int main()
{
int k=4,m=1,p;
p=func(k,m);
printf("%d",p);
p=func(k,m);
printf("%d\n",p);
}