C源程序是由函数组成的,一个程序往往由多个函数组成,函数是程序实现模块化变成的基本的单元,一般是为了完成某一个特定的功能,相当于其他语言中的子程序。一个较大程序的各项功能都是由各个子程序共同完成的,或者说C程序的全部工作都是由各式各样的函数完成的。
一个完整的C程序是通过函数之间互相调用实现的。通过对函数模块的调用,可以实现相应的功能。在进行程序设计时,会将一些常用的功能模块编写成函数,放在函数库中供公共使用。有些函数是按照ANSI C的要求随C语言编译器提供的,这种函数称为库函数。像printf、scanf等,都属于库函数。
每个程序的入口和出口都是位于main函数中。编写程序时,并不是将所有的内容都放在main方法中。为了方便规划、组织、编写和调试,一般的做法是将一个程序划分为若干个程序模块,每个程序模块都完成一部分功能。这样不同的程序模块可以由不同的人来完成,从而提高软件开发效率。
也就是说,main函数可以调用其他的函数,其他函数也可以互相调用。在main函数中调用其他的函数,这些函数执行完毕后又返回到man函数中。通常把这些被调用的函数称为下层函数。函数调用发生时,立即执行被调用的函数,而调用者则进入等待的状态,直到被调用函数执行完毕。函数可以由参数和返回值。
【注意】C程序中所有的函数都是平行的,即在定义函数时是互相独立的,在一个函数中不能嵌套定义另一个函数,函数间可以互相调用,但main函数是不能被调用的。
【示例1.1】在主函数中调用其他函数
#include
void Move();
void Build();
void Paint();
int main(){
Move();
Build();
Paint();
return 0;
}
void Move(){
printf("=============================\n");
printf("==========执行move函数===================\n");
printf("=============================\n");
}
void Build(){
printf("=============================\n");
printf("==========执行Build函数===================\n");
printf("=============================\n");
}
void Paint(){
printf("=============================\n");
printf("==========执行Paint函数===================\n");
printf("=============================\n");
}
下面分析一下这个程序:
首先,一个源文件是由一个或多个函数组成。一个源程序文件是一个编译单位,即以源程序为单位进行编译,而不是以函数为单位进行编译。
函数库由C系统提供,用户无须定义,在调用函数之前也不必再程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。例如,在上面的程序中用于控制台显示信息的printf函数,之前应在程序开始部分包含stdio.h头文件;又如,要使用其他字符串操作函数strlen、 strcmp等时,也应在程序开始部分包含string.h。
用于自定义函数,就是用户自己编写的用来实现特定功能的函数,如上面程序中的Move函数等。
在该程序中,要是printf函数首先要包含stdio.h头文件,之后声明3个自定义函数,最后在主函数中调用3个函数。
1、函数的定义形式
在程序中编写函数时,函数的定义是让编译器知道函数的功能。函数一般包括用户自己定义和系统提供的标准函数两种。如果是系统提供的标准函数,那么不需要进行定义即可使用,如果是用户自己定义的函数,那么必须遵守“先定义,后使用”的原则。函数的定义分两部分:函数头和函数体。函数的定义语法:
函数返回值的类型名 函数名(形式参数列表) --函数头
{
声明部分
语句部分 --函数体
return 表达式
}
函数头分三个部分:
返回值类型:返回值可以是某个C数据类型。
函数名:函数名也就是函数的标识符,函数名在程序中必须是唯一的。
参数列表:形式参数列表简称为形参,可以没有变量也可以有多个变量,在进行函数调用时,实际参数将被复制到这些变量中。有时,函数定义中没有形参,叫做无参函数,无参函数一般不用写返回值类型。
函数体:
函数体位于函数头下方,由一对大括号括起来,大括号决定了函数体的范围。函数体包括局部变量和函数的可执行代码。声明部分包括函数声明、变量定义和变量声明,函数要实现的特定功能,都是在函数体部分通过代码完成。最后通过return返回函数执行的结果。
2、函数的定义与声明
在程序中编写函数时,要先对函数进行声明,然后再对函数进行定义。函数的声明是让编译器知道函数的名称、参数、返回值类型等信息。函数的定义是让编译器知道函数的功能。
函数的声明格式由函数返回值类型、函数名、参数列表和分号4部分组成。例如:
返回值类型 函数名(参数列表);
【注意】如果将函数的定义放在调用函数之前,那么就不需要进行函数的声明。此时函数的定义就包含了函数的声明。
定义:实在编译器内创建了一个对象,为这个对象分配一块内存,并且给对象取一个名字,取得名字就是经常说的变量名或对象名。
声明:告诉编译器这个名字已经分配到一块内存里,并且这个名字现在归此声明所用,其他地方不允许再用这个名字。
定义和声明的最大区别:定义创建了对象,并给对象分配内存;声明没有为对象分配内存。
【示例2.1】计算任意两个整数的积
#include
int mul(int x,int y){
int z;
z = x * y;
return z;
}
int main(){
int a,b,c;
printf("请输入a和b :\n");
scanf("%d,%d",&a,&b);
c = mul(a,b);
printf("a 和 b 的乘积是:%d\n",c);
return 0;
}
在调用函数时,大多数情况下,主调函数和被调用函数之间有数据传递关系,这就是前面提到的函数形式。函数参数的作用是传递数据给函数使用,函数利用接收的数据进行具体的操作处理
在使用函数时,通常会涉及两种参数,形式参数和实际参数,两者之间既有联系又有区别。
形式参数:在定义函数时,函数名后面括号中的变量名称为“形式参数”。在函数调用之前,传递给函数的值将被复制到这些形参中。
实际参数:在调用一个函数时,也就是真正使用一个函数时,函数后面括号中的参数为“实际参数”。
在通常情况下,形式参数简称为形参,实际参数简称为实参。
形参和实参的特点和注意事项如下:
形参变量只有在被调用时才分配内存单元,在调用结束时,会释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能在使用该形参变量。
实参可以是常量、变量、表达式和函数等,不论是什么类型的数据,只要是实参,再到用进行调用时,这个类型量必须是确定的值,以便把这些值传递给形参。为了避免发生“类型不匹配”的错误,实参和形参的类型、数量、书写顺序都必须要保持一致。
C语言固定,实参变量对形参变量的数值传递是单向传递,即只能由实参传递给形参。
上面介绍了,在传递函数参数时,将变量作为函数的参数,除此之外还有一种特殊的情况,即数组作为函数参数。因为将数组作为函数参数进行传递,不同于标准的赋值调用的参数传递方法。下面将介绍数组作为函数参数时的参数传递方法。
当数组作为函数的参数时,只传递数组的地址,而不是将整个数组赋值到函数中去。当用数组作为实参调用函数时,指向该数组的第一个元素的指针就被传递到函数中。
例如:定义一个具有10个元素的整型数组:int array[10] ;
注意:在C语言中,没有任何下标的数组名就是一个指向该数组第一个元素的指针。其中的代码没有下标的数组名array与指向第一个元素的指针*array是相同的。
由于实参可以是表达式形式,数组元素可以是表达式的组成部分,因此数组元素可以作为函数的实参,与用变量作为函数实参一样,是单向传递。
【示例2.2.1 数组元素作为函数参数】
#include
void showMembers(int i);
int main(){
int array[10];
int i ;
for(i = 0;i<10;i++){
array[i] = i;
}
for(i = 0;i < 10;i++){
showMembers(array[i]);
}
return 0;
}
void showMembers(int i){
printf("显示的元素是:%d\n" ,i);
}
函数的调用方式有3种:函数语句调用、函数表达式和函数参数调用。
如果自定义函数在主函数的前面时,就不需要在引用头文件后再进行声明,如果自定义函数在主函数的后面时,就需要在引用头文件时进行提前声明。
把函数的调用作为一个语句就叫函数语句调用。函数语句调用是最长使用的调用函数的方式。
【示例3.1】函数语句调用:
#include
void disPlay(){
printf("hello,world!\n");
}
int main(){
disPlay();//函数语句调用
return 0;
}
函数出现在一个表达式中,这时要求函数的带回一个确定的值,这个值参加表达式的运算。
【示例3.2】函数表达式调用
#include
int addNum(int num1,int num2);
int main(){
int result;
int num3 = 10;
result = num3 * addNum(3,5);
printf("计算的结果是: %d \n",result);
return 0;
}
int addNum(int num1,int num2){
int result;
result = num1 + num2;
return result;
}
函数调佣可以作为一个函数的实参,将函数返回值作为实参传递到函数中进行使用。函数出现在一个表达式中,这时要求函数带回一个确定的值,这个值参加表达式的运算。
【示例3.3】函数参数调用:
#include
int addNum(int num1,int num2);
int main(){
int result;
result = addNum(20, addNum(3,2));
printf("result: %d \n",result);
return 0;
}
int addNum(int num1,int num2){
int result;
result = num1 + num2;
return result;
}
在C语言中,函数的定义都是互相平行、独立的。也就是说在定义函数时,一个函数体内不能包含定义另一个函数。但是可以嵌套调用函数,也就是说,在一个函数体内可以调用另一个函数。
【示例4】函数嵌套调用
#include
void a();
void b();
void c();
int main(){
a();
return 0;
}
void a(){
printf("a方法可以调用b方法\n");
b();
}
void b(){
printf("b方法可以调用c方法 \n");
c();
}
void c(){
printf("c方法被b方法调用 \n");
}