一.函数的定义
函数的定义就是编写一段程序,使该段程序完成所指定的操作。
函数定义的一般形式为:
类型标识符 函数名(类型 形式参数,类型 形式参数,...)
{ 声明部分
执行部分
}
类型标识符用于定义函数类型,即指定函数返回值类型。如果定义函数时默认类型标识符,则系统指定的函数返回值为int类型。如果函数执行后没有返回值,则函数类型标识符用void。
例如: 定义printstar函数
void printstar()
{
printf("********\n");
}
【拓展】在函数设计时有时会用到空函数
空函数的定义格式为:
类型说明符 函数名(){}
例如:
void dummy()
{}
二.函数的分类
1.从函数的定义角度来看,函数分为标准函数(库函数)和用户自定义函数。
(1)库函数。由C语言系统提供,用户无需定义,可直接使用。
(2)用户自定义的函数,由用户按需要编写的。
2.从函数的形式来看,函数分为无参函数和有参函数。
(1)无参函数。在函数定义,说明及调用中均不带参数,主调函数和被调函数之间不进行参数传送,此类函数用于完成一组指定的功能,可以返回或不返回函数值。例如printstar函数
(2)有参函数(带参函数)。在函数定义,说明中都有参数,称为形式参数(形参)。在函数调用时也必须给出参数,称为实际参数(实参)。主调函数把实参的值传送给形参,供被调函数使用。
3.从函数兼有其他语言中的函数和过程两种功能来看,函数分为有返回值函数和无返回值函数。
(1)有返回值函数。被调用执行完后向主调函数返回一个执行结果,称为函数返回值。
(2)无返回值函数。执行完后不向主调函数返回函数值。定义此类函数应用void定义函数为“空类型”或“无类型”
三.函数参数和返回值
1.形式参数和实际参数
形参:在定义函数时函数名后面的括号中变量名称为形参。
实参:在主调函数中调用一个函数时,函数名后面的括号中变量名称为实参。(可以是常量,变量或表达式)
在有参函数调用时,实参向形参传递参数。函数未被调用时,形参并不占据实际的储存单元,也没有实际值;只有函数被调用,系统才为形参分配储存单元,调用结束后,形参单元被释放。
【拓展】C语言中函数的值传递有两种方式:一种是传递数值,另一种是传递地址。
(1)值传递,即将实参的值传递给形参变量。由于形参与实参各自占据不同的储存空间,因此在函数体执行中,对形参变量的任何改变都不会改变实参的值。
例如:
#include<stdio.h>
void swap(float x,float y) //定义交换变量x,y值的函数(x,y为形参变量)
{
float temp;
temp=x;x=y;y=temp;
printf("x=%.2f y=%.2f\n",x,y);
}
void main()
{
float x=8.5,y=3.7; //此处的x,y为实参变量
swap(x,y); //调用swap函数
printf("x=%.2f y=%.2f\n",x,y);
}
运行结果为:
x=3.70 y=8.50
x=8.50 y=3.70
解释:swap函数交换的只是两个形参变量的值。函数调用时,实参的值传递给形参变量,形参变量x,y值交换,由于实参与形参在内存中占据不同的储存单元,实参值没有被交换,main函数中printf输出的是实参值。
(2)地址传递:函数调用时,将实参数据的储存地址作为参数传递给形参。
特点:形参与实参占据相同的内存单元,函数中对形参值的改变也会改变实参的值。函数参数的地址传递方式可实现调用函数与被调用函数之间的双向数据传递。
注意:实参和形参必须是地址常量或变量。比较典型的地址传递方式就是用数组名作为函数的参数。形参数组和实参数组为同一数组,共同拥有同一段内存空间。使用数组名作为函数参数时形参数组和实参数组的类型必须一致,长度可以不同(因为在函数调用时只传递数组的首地址,不检查形参数组的长度),多维数组可以作为函数的参数。
2.函数的返回值
函数的返回值是通过函数中的return语句获得的,return语句将被调用函数中的一个确定值带回主调函数中去。
return语句的一般形式为:
(1)return;
(2)return 表达式;或return(表达式);
return语句的作用是结束函数的执行,使控制返回到主调函数的调用点。带表达式的return语句会将表达式的值带回主调函数。
注意:(1)应在定义函数时指定函数返回值的类型。如果函数值类型和return语句中表达式的值不一致,则以函数类型为准。对于数值型数据,函数类型决定返回值类型。
例如:
int main(float a,float b) //函数值为整型
char letter(char c1,char c2) //函数值为字符型
double min(int y,int y) //函数值为双精度型
(2)return语句在函数体中可以有一个或多个,但只有其中一个起作用,一旦遇到return语句,就立即结束当前函数的执行,返回调用点。
(3)如果函数执行后没有返回值,则函数类型标识符用void。
四.函数的调用
1.调用有参函数的形式:
函数名(实参表列)
调用无参函数,没有实参表列,但括号不能省,形式为:
函数名()
如果实参表列含多个实参,则实参之间以逗号相隔,实参与形参的个数必须相等,类型匹配。
2.函数调用的方式
(1)表达式方式。函数调用出现在表达式中,必须要有一个明确的返回值以参加表达式运算。
c=2*max(a,b);
(2)参数方式。函数调用作为另一个函数调用的实参。
d=max(a,max(b,c))
(3)语句方式。函数调用作为一个独立语句,用于需要函数完成一定的操作,不要求带回返回值。比如:printstar函数的调用。
【拓展】函数的嵌套调用和递归调用
1.函数的嵌套调用
嵌套调用,就是函数在被调用过程中又去调用了其他函数。函数是不允许嵌套定义的,但允许嵌套调用。
例如:计算S=1^2!+2^2!+3^2!+4^2!.
#include<stdio.h>
long f1(int p) //定义一个计算平方值的函数f1
{ int k;
long r;
long f2(int);
k=p*p;
r=f2(k);
return r;
}
long f2(int q) //定义一个计算阶乘值的函数f2
{ long jc=1;
int i;
for(i=1;i<=q;i++)
jc=jc*i;
return jc;
}
main()
{ int i;
long s=0;
for(i=1;i<=4;i++)
s=s+f1(i);
printf("\ns=%ld\n",s);
}
2.函数的递归调用
递归求解问题的过程分为递推和回归两个阶段:
递推阶段是将原问题不断地转化成子问题,由未知向已知推进,最终达到已知解,则递推结束。
回归阶段是从已知解的问题出发,按照递推逆过程,逐一求值回归,到达递归开始处,结束回归。
五.函数的声明
函数声明的形式:
类型标识符 函数名(类型 参数名,类型 参数名,...);
或
类型标识符 函数名(类型 ,类型 ,...);
调用其他函数,调用前要对被调用函数进行函数声明。通过函数声明语句,向编译系统提供被调函数的信息包括函数返回值类型,函数名,参数个数及各参数类型等,这些称为函数原型。
注意:(1)主调函数定义的位置都在被调函数定义位置之后。这样,主调函数中可以省略对被调函数的声明。
(2)C语言对库函数的声明采用#include文件包含命令方式。使用时只需通过#include命令把“头文件”(stdio.h,math.h,string.h等)包含到程序中,即在程序中对这些库函数进行声明,用户就可以在程序中调用这些库函数了。