1
C语言程序设计
1.7.1.1 6.1.1 函数定义

6.1.1 函数定义

函数定义的一般形式。

1.无参函数的一般形式

类型说明符 函数名()

img496

其中,类型说明符和函数名称为函数头。类型说明符指明了函数返回值的类型,该类型说明符与本书前面介绍的各种说明符相同。函数名是由用户定义的标识符,函数名后有一个不可缺少的空括号。{}中的内容称为函数体。在函数体中也有类型说明,这是对函数体内部所用到的变量的类型说明。

定义函数时必须写返回值类型,如果函数没有返回值,此时函数类型符可以用关键字“void”说明返回值类型。

例如,我们可以这样定义一个无参函数:

例6-1 定义一个无参函数。

img497

这里,Hello是一个无参函数的函数名,当被其他函数调用时,输出Hello,how do you do字符串。

2.带参函数的一般形式

类型说明符 函数名(形式参数表)

形式参数类型说明

img498

有参函数比无参函数多了两个内容,其一是形式参数表,其二是形式参数类型说明,当前C编译器常把形式参数类型说明写入形式参数表中。在形参表中给出的参数称为形式参数(简称形参),它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值(简称实参)。形参既然是变量,当然必须给以类型说明,例如,定义一个函数,用于求两个数中较小的数,可写为:

例6-2 函数,求两个数中较小的数。

img499

第一行说明min函数是一个整型函数,其返回的函数值是一个整数。形参为整型变量a 和b。a和b的具体值是由主调函数在调用时传送过来的。在{}中的函数体内,除形参外没有使用其他变量,因此只有语句而没有变量类型说明。在min函数体中的return语句是把a(或b)的值作为函数的值返回给主调函数。有返回值函数中至少应有一个return语句。

在C程序中,一个函数的定义可以放在任意位置,既可放在主函数main之前,也可放在main之后。下面的例子就是把函数定义位置放在main之前。

例6-3 求两个数中较小的数。

img500

img501

考虑到在VC++开发环境中printf()函数调用需要#include〈stdio.h〉(TC2.0开发环境中printf()函数调用可省略#include〈stdio.h〉),所以本节的程序总有#include〈stdio.h〉。

现在我们可以从函数定义、函数原型说明及函数调用的角度来分析整个程序,从中进一步了解函数的各种特点。程序的第2行至第6行为m in函数定义。进入主函数后,因为准备调用m in函数,故先对min函数进行说明(程序第9行)。函数定义和函数原型说明并不是一回事,在后面还要专门讨论。可以看出函数原型说明与函数定义中的函数头部分相同,但是末尾要加分号。程序第13行为调用m in函数,并把x和y中的值传送给min的形参a和b。min函数执行的结果(a或b)将返回给变量z。最后由主函数输出z的值。

3.函数原型说明

在主调函数中调用某函数之前应对该被调函数进行说明,这与使用变量之前要先进行变量说明是一样的。在主调函数中对被调函数进行说明的目的是使编译系统知道被调函数返回值的类型及形参类型,以便在主调函数中按此种类型对返回值做相应的处理,在参数传递时对实参和形参类型进行检查。

对被调函数的说明的一般格式为:

  类型说明符 被调函数名(类型 形参,类型 形参,…);

或为:

  类型说明符 被调函数名(类型,类型,…);

C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数原型说明。

(1)如果被调函数的返回值是整型或字符型时,可以不对被调函数进行说明,而直接调用。这时系统将自动对被调函数返回值按整型处理。

(2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数进行说明而直接调用,例如例6-3中,函数m in的定义放在main 函数之前,因此可在main函数中省去对 min函数的函数原型说明“int min(int a,int b);”。

(3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明,例如:

char fun1(int a);

float fun2(float b);

main()

{

}

char fun1(int a)

{

}

float fun2(float b)

{

}

其中第一、二行对fun1函数和fun2函数预先进行了说明。因此在以后各函数中无需对fun1和fun2函数再进行说明就可直接调用。

对库函数的调用不再需要函数原型说明,但必须把该函数的头文件用include命令包含在源文件前部。

尽管在上述场合可以省去主调函数中对被调函数的函数原型说明。但在调用某函数f前,不先对函数f进行原型说明可能引起各种问题,如:

(1)如果编译程序后来遇到了f的定义,其返回值类型与它所假设不一致,编译程序有可能给出“函数重新定义”的错误信息。

(2)假设f在其他源文件中定义,或根本就是库函数,而且f的返回类型与默认假设不符,那么编译不会发现错误,连接时也不检查,产生的可执行程序在执行时则可能出错。

(3)如果函数调用的实参(个数或类型)与函数定义不一致,编译时不会发现错误,也不会自动生成类型转换,连接时也不检查,最终形成可执行程序里的语义错误。

因此,我们应坚持的正确原则是:

(1)如果使用库函数,那么就必须在源文件前部用#include命令包含必要的头文件。

(2)对所有未能在使用前给出定义的函数(无论它是定义在本文件后面,还是在其他源文件里),都应给出正确完整的函数原型说明。

(3)把原型说明写在源文件最前面(不要写在函数内部),以使函数的定义点和所有使用点都能“看到”同一个原型说明。如果坚持了这些原则,就能避免函数调用与定义不一致的错误。

良好的编程习惯6.1

img502 为了提高软件的可重用性,每个函数应该只完成一个较小的任务,并且函数名应该有效地表达任务尽量做到见名知义,这样的函数才能使程序更易于编写、测试、调试和维护。

img503 对自定义的函数,在任何情况下都写函数原型说明是一种良好的编程习惯,现在的VC++编译器就是这样要求的。