目录
前言
函数是C语言程序的基本模块,通过把相对独立的某个功能抽象出来成为一个独立实体(函数模块),并在程序中对函数按需调用以实现特定的功能。合理使用函数可以使程序变得更简短而清晰,有利于程序维护,提高程序开发效率,提高代码的重用性。
一、分类
根据定义划分
库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。printf 、scanf、getchar、putchar、gets、puts、strcat等函数均属此类。
用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行声明,然后才能使用。
根据返回划分
有返回值函数:函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。
无返回值函数:函数用于完成某项特定的处理任务, 执行完成后不向调用者返回函数值。
根据参数划分
无参函数:函数定义、函数说明及函数调用中均不带参数。
有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简称为形参);在函数调用时也必须给出参数,称为实际参数(简称为实参)。
二、函数的基本使用
函数定义
类型说明符 函数名(形参列表) //类型说明符指明函数的返回值类型
{ //{}中的内容称为函数体
语句 //函数若无返回,返回值类型为void,则无需用return语句返回
…… //函数若有返回,则需用return语句返回一个与返回值类型匹配的值
} //有参函数比无参函数多一个形参列表,各参数之间用逗号间隔。
函数调用
函数名(实参列表) //实参可以是常量、变量、表达式、函数等,之间用逗号分隔,对无参函数调用时则无实参表
在C语言中,可以用以下几种方式调用函数:
1.函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。
z = max(x,y); //把max的返回值赋予变量z
2.函数语句:函数调用的一般形式加上分号即构成函数语句。
printf("%d", a); //以函数语句的方式调用函数
scanf("%d", &b);
3.函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。
printf("%d", max(x,y)); //把max的返回值作为printf函数的实参来使用的
PS:函数调用中发生的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反向传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
函数的嵌套调用
C语言中不允许作嵌套的函数定义,各函数之间是平行的,定义时互相独立,不存在上一级函数和下一级函数的问题。 但是C语言允许在一个函数的定义中出现对另一个函数的调用。 这样就出现了函数的嵌套调用,即在被调函数中又调用其它函数。 这与其它语言的子程序嵌套的情形是类似的。
函数的递归调用
一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。C语言允许函数的递归调用。在递归调用中,主调函数又是被调函数,执行递归函数将反复调用其自身,每调用一次就进入新的一层。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。
三、函数原型
概念
函数定义是指对函数功能的确立,包括指定函数名、函数返回类型、形参及类型、函数体等,它是一个完整的、独立的函数单位。
函数原型声明则是把函数的名字、函数返回类型以及形参的个数、类型和顺序(PS:不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致)。
声明
类型说明符 函数名(类型 形参, 类型 形参 ……);
或者
类型说明符 函数名(类型, 类型 ……);
作用
根据函数原型在程序编译阶段对调用函数的合法性进行全面检查。如果发现与函数原型不匹配的函数调用就报告编译出错,它属于语法错误。用户根据屏幕显示的出错信息很容易发现和纠正错误。
函数原型定义了一种标准的行为,具体的功能实现有函数定义去完成。并且同一个函数原型可由多个用户各自编写的函数定义实现不同的功能。
使用注意点
1、应当保证函数原型与函数定义写法上的一致,即函数返回类型、函数名、参数个数、参数类型和参数顺序必须相同。在函数调用时函数名、实参类型和实参个数应与函数原型一致。
2、当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再做声明而直接调用。
3、当被调函数的函数定义出现在主调函数之后时,则必须在调用此函数之前对被调用的函数作声明,目的就是在函数尚未定义的情况下,事先将该函数的有关信息通知编译系统,以便使编译能正常进行。
4、如在所有函数定义之前,在函数外预先声明了各个函数,则在以后的各主调函数中,可不再对被调函数作声明。
5、如果被调函数的返回值是整型或字符型时,不管定义在主调函数之前或之后,可以不对被调函数作声明而直接调用,这时系统将自动对被调函数返回值按整型处理。
6、对C的标准库函数的调用不需要再作声明,但必须把该函数的头文件用#include命令包含在源文件前部。
7、养成对所有用到的函数作声明的习惯,这是保证程序正确性和可读性的重要环节。
四、函数参数求值的顺序和二义性
函数参数的求值顺序
当一个函数带有多个参数时,C没有规定在调用时实参的求值顺序,并允许不同的编译系统根据代码优化的需要,自行规定对实参的求值顺序。从左至右还是从右至左求值,其输出顺序都是不变的,即输出顺序总是和实参表中实参的顺序相同。有些编译器,如Turbo C,是自右至左求值。这都取决于编译系统。
二义性的产生和避免
求值顺序的不确定性会导致同一程序在不同的编译系统下产生二义性。
int main(void)
{
int x = 100, y = 20;
int s = sum_int(++x, x+y);
printf("s:%d\n", s);
return 0;
}
int sum_int(int i, int j)
{
return i+j;
}
运行上述代码,若输出“s:222”表示求值顺序从左至右,若输出“s:221”表示求值顺序从右至左。
在进行函数调用时,实参都必须具有确定的值这样才可以避免二义性的产生。