9.2 函数声明和函数原型
知识点讲解:视频\第9章\必须知道的函数声明和函数原型.mp4
在前面的内容中,曾经多次提到了声明和定义,例如声明变量和定义变量。在大多数情况下,开发人员和读者会对上述两个名词混为一谈。实际上它们的意义基本上也是相同的,但是从严格意义上讲,二者是完全不同的概念。
“定义”是指对函数功能的确立,包括指定函数名、函数值类型、形参类型、函数体等,它是一个完整、独立的函数单位。而“声明”的作用则是把函数名字、函数类型以及形参类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。从程序中可以看到函数声明与函数定义中的函数首部基本上是相同的。因此可以简单地照写已定义的函数首部,再加一个分号,就成为函数的“声明”。在函数声明中也可以不写形参名,而只写形参的类型。
在C语言中,函数声明称为函数原型(function prototype)。使用函数原型是ANSI C的一个重要特点。它的作用主要是利用其在程序编译阶段对调用函数的合法性进行全面检查。
以前C版本的函数声明方式不采用函数原型,而只是声明函数名和函数类型。例如“float add();”不包括参数类型和参数个数。系统不检查参数类型和参数个数。新版本也兼容这种用法,但不提倡这种用法,因为它未进行全面检查。
实际上,如果在函数调用前,没有对函数进行声明,则编译系统会把第一次遇到该函数的形式(函数定义或函数调用)作为函数声明,并将函数类型默认为整型。如果一个max函数在调用之前没有进行函数声明,则编译时首先遇到的函数形式是函数调用“max(a,b)”,由于对原型的处理是不考虑参数名的,因此系统将max()加上int作为函数声明,即“int max();”因此不少教材说,如果函数类型为整型,则在函数调用前不必进行函数声明。但是使用这种方法时,系统无法对参数的类型进行检查。并且在调用函数时参数使用不当,编译时也不会报错。因此,为了程序清晰和安全,建议都加以声明为好。
如果被调用函数的定义出现在主调函数之前,则可以不必加以声明。因为编译系统已经提前知道了已定义的函数类型,它会根据函数首部提供的信息对函数调用进行正确性检查。
如果在所有函数定义之前,在函数的外部进行了函数声明,则在各个主调用函数中不必对所调用的函数再进行声明。
1.函数声明
在调用用户自定义函数时,需要满足以下两个条件。
(1)必须已经定义了被调用函数。
(2)如果被调用函数与调用它的函数在同一个源文件中,则一般在主调函数中会对被调用函数进行声明。函数声明的一般格式如下所示:
函数类型 函数名(形参类型1形参名1,形参类型2形参名2, …)
在下列3种情况下可以省略函数声明。
(1)函数定义的位置在主调函数之前。
(2)函数的返回值为整型或字符型,且实参和形参的数据类型都为整型或字符型。
(3)如果在所有函数定义之前,在函数的外部已进行了函数声明,则在各个主调函数中不必对所调用函数再进行声明。
例如,求s=(1+2+3+…+n)/(1+2+3+…+m)的值,其中n和m为整数,其实现代码如下所示。
#include <stdio.h> float sum(int k) /* 定义函数sum。函数sum定义在前,调用在后*/ { float q=0.0; int a; for(a=1; a<=k; a++) q+=a; return(q); } int main(void) /* 虽然函数sum为单精度型,但由于sum的定义在调用之前 */ { /* 所以在main中调用sum函数时不需再进行函数声明*/ int n, m; float s; printf("输入n和m:\n"); scanf("%d, %d", &n, &m); s=sum(n)/sum(m); printf("s=%.2f\n", s); }
2.函数原型
在声明被调函数时,编译系统需知道被调函数有几个参数,各自是什么类型,而参数名称是无关紧要的,因此对被调函数的声明可以简化成以下形式。
函数类型 函数名(形参类型1,形参类型2, ……);
在C语言中,上面的函数声明称为函数原型。在程序中使用函数原型的主要作用是在编译源程序时对调用函数的合法性进行全面检查。当编译系统发现函数原型与函数调用不匹配(如函数类型不匹配、参数个数不一致、参数类型不匹配等)时,就会在屏幕上显示出错误信息,用户可以根据提示的出错信息发现并改正函数调用中的错误。