**
第9章 函数
**
大型程序一般会被分为若干个程序模块,每个模块实现一个特定功能 。C语言中,由函数实现子程序,由子程序实现模块功能。本章致力于使读者了解函数的概念,掌握函数的定义及调用方式;了解内部函数和外部函数的作用范围,能区分局部变量和全局变量的不同;最后能将函数应用于程序中,将程序分成不同的功能模块。本章的知识架构及重难点如下:
9.1 函数概述
构成C程序的基本单元是函数,函数中包含着程序的可执行代码。每个C程序的入口和出口都位于main函数中,但并不需要把所有指令都放在main函数中。一般的做法是将程序划分成若干个模块,每个模块完成一部分功能,不同的程序模块可以由不同的人来完成,从而提高软件开发的效率。这就好比是盖楼房,一栋摩天大楼是不可能靠一个人完成的,而要靠多部门、多工种之间协力完成。通常是有一个总工程师,在他的指挥下,有部门运输建筑材料,有部门建造楼房主体,还有部门粉刷内外墙涂料。编写程序的道理与盖楼是一样的,主函数就像总工程师一样,控制着整体程序的推进和执行,其中定义的其他函数就好比参与盖楼的多个部门或工种,他们要通过某种调度(函数调用)才能完成特定的功能。
主函数可以调用其他函数,其他函数间也可以相互调用。函数可以有参数和返回值,通过它们实现数据间的传递。在主函数中调用其他函数,这些函数执行完毕之后会返回main函数中。通常把这些被调用的函数称为下层函数。函数调用发生时,立即执行被调用的函数,而调用者则进入等待的状态,直到被调用函数执行完毕。
【例9.1】编写3个函数:做饭,钓鱼,写诗,在本实例中,定义3个函数来完成做饭、钓鱼、写诗等待定的功能,然后在主函数中调用它们。为了简化函数的功能,这里只让其输出一条提示信息。读者可通过本实例对函数有一个直观的认识。

在分析本实例之前,我们先来了解一下什么是C程序源文件、库函数和用户自定义函数。
☑源文件:由一个或者多个函数组成。C语言以源程序为单位进行编译,而不是以函数为单位进行编译。
☑库函数:由C语言系统提供,用户无须定义,调用前也不必做类型说明,但需要在程序开始部分包含有该函数原型的头文件。例如,要使用能在控制台显示信息的printf函数,需在程序开始时包含stdio.h头文件;要使用字符串操作函数strlen、strcmp等时,需在程序开始时包含string.h头文件。
☑用户自定义函数:用户编写的用来实现特定功能的函数。例如,Cook、Fish和Poem函数都是自定义函数。
在本例程序中,首先包含了stdio.h头文件,然后声明了3个自定义函数,最后在主函数main中调用了这3个函数。在主函数main外,可以看到这3个函数的定义。
运行程序,显示效果如下图所示。
编程训练:
训练1:盖楼房 编写3个函数,分别实现搬运建筑材料、建造楼房主体、粉刷内外墙功能,并在主函数中调用这3个函数。输出结果如下 :
执行搬运功能
执行建造功能
执行粉刷功能
训练2:写情书 定义一个函数,内定为一封情书,在主函数中调用该函数,将情书内容展示出来。运行结果如下:
人生最美好的是相遇
我一生最奢侈的事
就是途中与你相遇
然后相濡以沫,共闻花香,有生之年
只诉温暖不言歾,倾心相遇,安暖相陪
9.2 函数的定义和声明
C语言的库函数可以直接调用,如printf输出函数。而自定义函数则必须由用户进行定义,确定其要实现的功能,这样才能被其他函数调用。
**
9.2.1 函数的定义
**
一个函数应包括函数头和函数体。定义一个函数的语法格式如下:
首先来看一段代码,然后通过分解,了解函数的构成。
1.函数头
函数头是函数的入口,标志着一段函数代码的开始。函数头包括返回值类型、函数名和参数列表3个部分,如下图所示。
☑返回值类型:函数返回值的类型,必须是C语言中的某个数据类型。这里函数的返回值将是一个int型值。
☑函数名:函数的标识符,在一个C程序中应保持唯一。因为是标识符,所以函数名要遵守标识符命名规则。这里,函数名是AddTwoNumber,可以推测出该函数的功能是两数相加求和。
☑参数列表:调用函数时,用于将主调函数中的实际参数复制到该列表对应的形式参数中。可以没有参数,也可以有多个参数。这里定义了两个int变量,表示要相加的两个数。
2.函数体
函数体位于函数头的下方位置,由一对大括号括起来,大括号决定了函数体的范围。函数要实现的特定功能,都是在函数体部分通过代码语句完成的,最后通过return语句返回实现的结果。
在上面的代码中,函数体内首先定义了一个int型变量,用来保存加法的计算结果,之后利用传递进来的参数进行加法操作,并将结果保存在result变量中,最后函数要将所得到的结果进行返回。通过这些语句的操作,实现了求解两数和的特定功能。
在定义函数时会出现以下几种特殊的情况。
☑无参函数:没有参数列表的函数。如例9.1中的Cook、Fish、Poem都是无参函数。
☑空函数:没有任何内容,也没有什么实际功能的函数。空函数的形式如下:
类型说明符 函数名()
{
}
实际开发中,有时某个函数还未编好,或者后续要拓展某个函数,这时就会先用一个空函数代替,先占个位置,待后续时机成熟再用编好的函数取代它。
注意,C语言中,函数的定义是互相平行、独立的。也就是说,函数体内不能再包含其他函数的定义。例如,下面的代码是错误的:
int main()
{
void Display() /*错误!不能在函数体内定义另一个函数*/
{
printf("I want to show the Nesting function");
}
return 0;
}
这里,主函数main中定义了一个Display函数,目的是输出一句提示。由于C语言不允许进行嵌套定义,因此编译时会出现如图9.3所示的错误提示。
9.2.2 函数的声明
在程序中编写函数时,要先对函数进行声明,再对函数进行定义。函数定义是为了让编译器知道函数的功能 ,而函数声明是为了让编译器预先知道有这么一个函数,以及函数的名称、参数、返回值类型等信息。
函数声明的一般形式如下:
返回值类型 函数名(参数列表);
要注意的是,函数声明语句的最后要用分号";"作为结尾。例如,声明一个函数的代码如下:
Int ShowNumber(int iNumber);
【例9.2】交换两个数值,通过本实例了解函数声明与函数定义的位置,及其在程序中的作用。
(1)观察上面的程序,可以看到在main函数的开头先进行了exchange函数的声明,声明的作用是告知其函数将在后面进行定义。
(2)在main函数体中,首先定义两个整型变量a、b,之后输出一条提示消息,然后调用exchange函数。
(3)在main函数的定义之后可以看到exchange函数的定义,功能是实现两个数的数值互换。
运行程序,结果如下图所示。
注意:如果先定义函数,再调用函数,则不再需要进行函数声明,此时函数定义已经包含了函数声明的作用。
编程训练:
训练3:输出谚语 编写一个程序,输出“最大的挑战和突破在于用人,而用人最大的突破是信任人”。运行效果如下:
。主函数中,通过“iResult=Minus(9,4); "语句调用Minus函数,这里9和4这实际参数,调用函数后,将用4代替iNumber1,用9代替iNumber2,因此Minus函数实际执行的运算的是9-4。
注意,函数参数可以是常量、变量、数组、指针等,也可以是表达式。
9.3.2 数组作函数参数
1.数组元素作为函数参数
【例9.3】输出数组元素
定义一个数组并为其赋值,然后将数组元素作为函数实参进行传递。自定义函数体中,形参得到实参传递的数值后,将其显示输出。
(1)首先进行函数声明,在主函数main中定义一个整型数组和一个整型变量i。
(2)使用for循环语句对数组中的元素依次赋值,在这里,变量i既是循环条件,也是引用数组元素的下标。
(3)通过for循环语句调用ShowMember函数,显示数据。
运行程序,显示效果如上图所示。
**
2.数组名作为函数参数
**
C语言中,数组名表示的是数组中第一个元素的地址 。因此,当数组名作为函数实参时,传递的是数组的地址。这点和数组元素做实参时是不一样的,注意体会其不同。
【9.4】数组名作为函数参数
在本实例中,使用数组名作为函数的实参和形参,实现数组的赋值和输出。
(1)首先对Evaluate函数和Display函数进行声明,在声明语句中可以看到数组名作为形参。
(2)在主函数main中定义一个具有10个元素的整型数组iArray。
(3)调用Evaluate函数,数组名iArray作为函数实参,传递的是数组的地址。在Evaluate函数中,使用数组iArrayName作为形参,接受对应的地址空间,并对数组进行赋值操作。
(4)调用Display函数,将数组输出,可以看到在函数参数中使用的也是数组名称。运行程序,显示效果如上图所示。