引子
为什么要用到函数?什么是模块化程序设计?
:我们通常将相对独立经常使用的功能抽象为函数。
函数编写完成以后可以重复使用,使用时只需要关心程序的功能而不必关心函数功能的具体实现。这样有利于代码重用,可以提高开发效率,也便于分工合作以及修改维护。这就是模块化程序设计。
我们通过例子来理解模块化程序设计,要求用函数调用实现输出以下的结果:
*************
how do you do!
*************
#include<stdio.h>
#include<string.h>
int main(){
void printstar(); //声明 printstar 函数
void print_message(); //声明 print_message 函数
printstar(); //调用 printstar 函数
print_message(); //调用 print_message 函数
printstar(); //调用 printstar 函数
return 0;
}
void printstar(){ //定义 printstar 函数
printf("**************\n");
}
void print_message(){ //定义 print_message 函数
printf("how do you do!\n");
}
总结知识点
-
定义函数:
两种定义:有参/无参
基本格式:类型名 函数名(形式参数列表/可空){函数体}
类型名表示的是返回值的类型;
例如:指定函数的类型为 void,表示函数无类型,即无函数值,也就是说,执行这两个函数不会返回任何值到 main 函数。位置:main函数外部;
规范:指定他的名字、函数返回类型、函数实现的功能以及参数的个数和类型
-
声明函数:
基本格式: 函数类型 函数名 (参数);
位置:main函数之前或者main函数内部开头部分;
作用:函数声明的作用是把有关函数的信息(函数名、函数类型、函数参数的个数与类型)通知编译系统,以便于在编译系统对程序进行编译时,在进行到 main 函数调用 printstar 和 print_message 时知道他们是函数而不是变量或其他对象。
-
调用函数:
基本格式:函数类型 函数名();
-
库函数
对于 C 编译系统提供的库函数,是由编译系统事先定义好的,库文件中包含了对各函数的定义。程序设计者不必自己定义,只需要用 #include 指令把有关的头文件包含到本文件中即可。例如在程序中用到 sqrt,fabs,sin 等数学函数,就必须在文件模块开头写上:#include<math.h>。 -
参数
在定义函数的时候函数名后面括号中的变量名称为“形式参数”(简称“形参”)
在调用语句 c=max(a,b); 中,a 和 b 称为“实际参数”(简称“实参”)。
举个例子:
#include<stdio.h>
int main(){
int max(int x,int y); //声明 max 函数
int a = 10,b = 20;
int c;
//调用max函数
c = max(a,b); //a, b 为实际参数;调用max函数,传递的是a,b的值给 max 函数。
printf("%d",c);
return 0;
}
//定义max函数:
int max(int x,int y){ //int 代表的是返回值是 int 型
int z;
z = x>y ? x:y;
return (z); //返回 z
}
-
返回值
函数可以通过 return 语句将运算得到的 z 的值带回主调函数。应当注意返回值的类型和函数类型一致。如 max 函数为 int 型,返回值是变量 z,也是 int 类型,两者一致;函数分类:
1) 函 数 = { 库 函 数 用 户 自 定 义 函 数 函数=\left\{ \begin{matrix} 库函数 \\ 用户自定义函数 \end{matrix} \right. 函数={库函数用户自定义函数
2)
函 数 = { 无 参 函 数 有 参 函 数 函数=\left\{ \begin{matrix} 无参函数\\ 有参函数 \end{matrix} \right. 函数={无参函数有参函数
注:
无参函数:调用时,主调函数不向被调函数传递数据。无参函数一般用来执行指定的一组操作,比如 printstar 就是输出一行 “*”。无参函数可以带回也可以不带回函数值。
有参函数:在调用函数时,主调函数通过参数向被调函数传输数据,例如 strlen(测字符串长度的函数) 就是有参函数,主函数传递一个字符串到 strlen 函数,strlen 返回值是这个字符串的长度。函数的嵌套调用与递归调用:
1.C 语言的函数定义是互相平行、独立的,也就是说,在定义函数时,一个函数内不能定义另一个函数。
但是,在定义一个函数A的时候,可以调用另一个已经定义的函数B(调用的时候必须在A中先声明函数B)。2.在调用一个函数的过程中又出现直接或间接的调用该函数本身,称为函数的递归调用(递归函数的两个要素为递归表达式和递归出口。)
例如:定义一个阶乘算法:n!
f ( x ) = { 1 , n = 0 , 1 n ∗ ( n − 1 ) ! , n > 1 f(x)=\left\{ \begin{matrix} 1 , n=0,1\\ n*(n-1)! ,n>1 \end{matrix} \right. f(x)={1,n=0,1n∗(n−1)!,n>1
int fac(int n){
int f;
if(n==0||n==1)
f = 1;
else
f = fac(n-1) * n;
return f;
}
数组与函数
数组元素可以作为一个函数的实参,不能作为形参。
例如:定义一个Max函数:
int max(int x,int y){
return(x>y?x:y);
}
在主函数中定义一个变量 m,m 的初值为 a[0],每次调用 max 函数后的返回值存放在 m 中。用打擂台算法,将数组元素 a[1] 到 a[cnt] 与 m 比较,最后的到的 m 就是最大值。
打擂台:
for(i=1,m=a[0],n=0;i<10;i++){
if(max(m,a[i])>m){ //若 max 函数返回值大于 m
m = max(m,a[i]); //max 函数的返回值取代 m
n = i; //把数组元素的序号记下来,放到 n 中
}
}
除了可以用数组元素作为函数的参数外,还可以用数组名做函数参数(包括实参和形参)。
应当注意的是:用数组元素作为实参时,向形参变量传递的是数组所对应数组元素的值,而用数组名做函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。
例题:有一个一维数组 score,内放 10 个学生成绩,求平均成绩。
(使用自定义ave函数求平均值)
不完全代码:
float score[10];//在主调函数中声明该数组
aver = average(score); //注意这里调用函数时,向形参传递的是数组首元素的地址
//定义ave函数
float average(float array[10]){
int i;//数组下标
float aver,sum = 0;
for(i=0;i<10;i++)//遍历数组
sum = sum + array[i];
aver = sum / 10;
return(aver);
}
注意:用数组名作为函数参数,应该在主调函数和被调用函数分别定义数组,例如 array 是形参的数组名,score 是实参数组名,分别在其所在函数中定义。