如果程序的功能比较多,规模比较大,把所有的程序代码都写在一个主函数(main函数)中,就会使主函数变得庞杂、头绪不清,是阅读和维护程序变得困难。此外,有事程序中要多次实现某一功能(例如打印每一页的开头),就需要多次重复编写实现此功能的程序代码,这使程序冗长、不精练。
因此,人们想到采用“组装”的办法来简化程序设计的过程。这就是模块化程序设计的思路。
“函数”是从英文function翻译过来的,其实function在英文中的意思既是“函数”,也是“功能”。从本质意义上来说,函数就是用来完成一定的功能的。所谓函数名就是给该功能起一个名字。
注意: 函数就是功能。每一个函数用来实现一个特定的功能。函数的名字应反映其代表的功能。
在设计一个较大的程序时,往往把它分成若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。一个C程序可由一个主函数和若干个其他函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。
举个例子【例 7.1】想输出一下的结果,用函数调用实现。
*************************
How do you do!
*************************
编写程序:
#include <stdio.h>
int main() {
void print_star(); //声明函数
void print_message();
print_star(); //调用函数
print_message();
print_star();
return 0;
}
void print_star(){
//定义函数
printf("******************\n");
}
void print_message(){
printf(" How do you do!\n");
}
运行结果:
**说明:**为什么要用函数
(1)一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对较大的程序,一般不希望把所有内容全放在一个文件中,而是将他们分别防放在若干个原文件中,由若干个源程序文件组成一个C程序。这样便于分别编写和编译,提高调试效率。一个源文件可以为多个C程序公用。
(2)一个源文件由一个或多个函数以及其他有关内容(如指令、数据声明与定义等)组成。一个源文件是一个编译单位,不是以函数为单位进行编译的。
(3)C程序的执行时从main函数开始的,如果在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。
(4)所有函数都是平行的,即在定义函数时是分别进行的,是相互独立的。一个函数并不从属于另一个函数,即函数不能嵌套定义。函数建可以相互调用,但不能调用main函数。main函数是被操作系统调用的。
(5)从用户使用的角度看,函数由两种。
①库函数,它是由系统提供的,用户不必自己定义,可直接使用它们。不用的C语言编译系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。
②用户自己定义的函数。
(6)从函数的形式看,函数分为两类。
①无参函数。在调用无参函数时,主调函数不向被调函数传递数据。无参函数可以带回或不带回函数值,但一般以不带回函数值的居多。
②有参函数。在调用函数时,主函数在调用被调函数时,通过参数向被调函数传递数据,一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。
C语言要求,在程序中用到的所有函数,必须“先定义,后使用”。
定义函数应包括以下几个内容:
(1)指定函数的名字,以便以后按名调用。
(2)指定函数的类型,即函数返回值的类型。
(3)指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项。
(4)制定函数应当完成什么操作,也就是函数是做什么的,即函数的功能。
1.定义无参函数
定义无参函数的一般形式为
类型名 函数名()
{
函数体
}
或
类型名 函数名(void)
{
函数体
}
函数名后面括号内的void表示“空”,即函数没有参数。
函数体包括声明部分和语句部分。
在定义函数时要用“类型标识符”(即类型名)指定函数值类型,即指定函数值带回来的值的类型。
2.定义有参函数
例,以下定义的max函数是有参函数:
int max(int x,int y)
{
int z; //声明部分
z=x>y?x:y; //执行语句部分
return(z);
}
定义有参函数的一般形式为
类型名 函数名(形式参数表列)
{
函数体
}
函数体包括声明部分和语句部分。
3.定义空函数
空函数的形式为
类型名 函数名()
{
}
函数体是空函数,调用才函数时,什么工作也不做,没有任何实际作用。
函数调用的一般形式为
函数名(实参表列)
如果是调用无参函数,在“实参表列”没有,但括号不能省略。
1.函数调用语句
把函数调用单独作为一个语句。如“print_star();”。这时不要求函数带回值,只要求函数完成一定的操作。
2.函数表达式
函数调用出现在另一个表达式中。此时要求函数带回一个确定的值以参加表达式的运算。
3.函数参数
函数调用作为另一个函数调用的实参。例如m=max(a,max(b,c));又如printf("%d",max(a,b));
**说明:**调用函数并不一定要求包括分号,只有作为函数调用语句才需要分号。如果作为函数表达式或函数参数,函数调用本身是不必有分号的。
1.形式参数和时间参数
在调用有参函数时,主调函数和被调函数之间有数据传递关系。在定义函数时函数名后面括号中的变量名称为“形式参数”(简称“形参”)或“虚拟参数”。在主调函数中调用一个函数时,函数名后面括号内的参数称为“实际参数”(简称“实参”)。实际参数可以是常量、变量或表达式。
2.实参和形参之间的数据传递
在调用函数过程中,系统会把实参的值传递给被调函数的形参。或者说,形参从实参中得到值。
在调用函数过程中发生的实参与形参间的数据传递称为“虚实结合”。
举个例子【例 7.2】输入两个整数,要求输出其中较大值。要求用函数来找到大数。
编写程序:
#include <stdio.h>
//输入两个整数,要求输出其中较大值。要求用函数来找到大数。
int max(int x,int y) //定义max函授,有两个参数
{
int z; //定义临时变量z
z=x>y?x:y; //把x和y中的大值赋值给z
return (z); //把z作为max函数的值带回main函数
}
int main()
{
int max(int x,int y); //对max函数声明
int a,b,c;
printf("please enter two integer numbers:\n"); //提示输入数据
scanf("%d,%d",&a,&b); //输入两个整数
c=max(a,b); //调用max函数,有两个实参。大值赋给变量c
printf("max is:%d",c); //输出c的值
return 0;
}
运行结果:
说明:
(1)实参可以是常量、变量或表达式,例如:max=(3,a+b),但是要求它们有确定的值。
(2)实参与形参的类型相同或赋值兼容。如果实参和形参类型不同或者相反,则按不同类型数值的赋值规则进行转换。
(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,被调函数的形参才被临时分配内存单元。
(2)将实参的值传递给对应的形参。
(3)在执行被调函数期间,由于形参已经有值,就可以利用形参进行有关的运算。
(4)通过return语句将函数值带回到主调函数。应当注意,返回值的类型与函数类型一致。如果函数不需要返回值,则不需要return语句。这时函数的类型用定义为void类型。
(5)调用结束,形参单元被释放。注意:实参单元仍保留并维持原值,没有改变。
注意: 实参向形参的数据传递是“值传递”,单向传递,只能由实参传给形参,而不能由形参传给实参。实参和形参在内存中占有不同的存储单元,实参无法得到形参的值。
通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数值(函数的返回值)。
说明:
(1)函数的返回值是通过函数中的return语句获得的。 return语句将被调函数中的一个确定值带回到主调函数中。如果需要从被调函数带回一个函数值(供主调函数使用),被调用函数中必须包含return语句。如果不需要,则被调用函数可以不要return语句。
一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个return语句就起作用。
(2)函数值的类型。既然函数有返回值,这个值应当属于某一个确定的类型,应当在函数定义值指定函数值的类型。
(3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。 如果函数值的类型和return语句中的表达式的值不一致,则以函数类型为准。即函数类型决定返回值的类型。
(4)对于不带回值的函数,应当用定义函数为“void”类型(或称“空类型”)。
在一个函数中调用另一个函数需要具备以下条件:
(1)首先被调用的函数必须是已经定义的函数(库函数或用户自己定义的函数)。
(2)如果使用库函数,应该在本文件开头用#include指令将调用有关库函数所需用到的信息“包含”到本文件中。
(3)如果使用用户自己定义的函数,而该函数的位置在主调函数的后面(在同一文件中),应该在主调函数中对被调用的函数做声明(declaration)。
举个例子【例 7.3】输入两个实数,用一个函数求出他们的和。
编写程序:
#include <stdio.h>
//输入两个实数,用一个函数求出他们的和。
int main() {
int a,b,c;
int sum(int x,int y);
printf("please enter two integer numbers:\n");
scanf("%d,%d",&a,&b);
c=sum(a,b);
printf("this two numbers' sum is:%d",c);
return 0;
}
int sum(int x,int y){
int s;
s=x+y;
return s;
}
运行结果:
可以发现,函数的声明和函数定义中的第一行(函数首部)基本上是相同的,只差一个分号(函数声明比函数定义中的首行多一个分号)。因此写函数声明的时候,可以简单地照写以定义的函数的首行,在加一个分号,就成了函数的“声明