在使用函数之前应该先声明,事先通知编译器该函数的类型:换句话说,一个声明即是描述一个函数的接口。声明至少应指明函数返回值的类型,如下例所示:
int rename();
这一行代码声明 rename()是一个函数,其返回值的类型是 int 类型。因为在默认情况下,函数名是外部可见的标识符,所以上述声明等同于:
extern int rename();
该声明没有包含关于函数参数的数量和类型等相关信息。因此,编译器无法检查调用该函数时所传入的参数是否正确。如果调用该函数时传入的参数有别于该函数的定义,那么会导致严重的运行错误。
为了避免这样的错误,应该在声明函数时,同时声明函数的参数。换句话说,所声明的应该是一个函数原型(function prototype)。例如,标准库函数 rename()(它用于修改文件名)的原型如下:
int rename( const char *oldname, const char *newname );
该函数需要两个参数,类型都是指向 const char 的指针。换句话说,此函数使用这两个指针的目的是为了读取 char 对象,所以参数可以是字符串字面量。
在原型声明中,参数的标识符是可选的,可以不写。如果将参数名包含进去,这些名称的作用域也仅限于该原型中。对于编译器来说参数名是没有意义的,它们最多和注释一样告诉程序员该参数的目的。
例如,在函数 rename()的原型中,参数名称 oldname 和 newname 用来告诉程序员,在调用函数时,旧的文件名写在前而,新的文件名写在后面。对于编译器来说,上述原型声明等同于没有给出参数名的声明:
int rename( const char *, const char * );
标准头文件中包含了标准库函数的原型。如果想在程序中调用函数 rename(),可以通过在代码中包含头文件 stdio.h 的方式来达到声明的效果。
通常,可以将自己所定义的函数原型放在一个头文件中,这样在其他任何源代码文件中,通过 include 命令来包含该头文件,则可以使用这些函数。
C 语言允许定义参数数量可变(variable number of argument)的函数。关于此类函数,最常见的例子是 printf(),它具有下面的原型:
int printf( const char *format, ... );
如同该例所展示的,这个参数类型列表的最后一个逗号后,跟着的是省略号(…)。省略号代表了可选参数。函数 printf()调用的第一个参数必须是 char 指针。这个参数后面可能会跟着其他参数。原型并没有包含可选参数的数量或类型等相关信息。
当声明某个函数,其参数是长度可变数组(variable-length array),除了在函数定义的头部,其他地方都可以使用星号(*)来表示数组长度。如果使用非常量的整数表达式来定义数组长度,编译器会将它视为星号。例如,下面所有的声明都可当作函数 maximum()的原型:
double maximum( int nrows, int ncols, double matrix[nrows][ncols] );
double maximum( int nrows, int ncols, double matrix[ ][ncols] );
double maximum( int nrows, int ncols, double matrix[*][*] );
double maximum( int nrows, int ncols, double matrix[ ][*] );