C Primer Plus 第9章 函数 9.2 ANSI C 的函数原型

9.2.1 产生的问题函数

下面咱们讨论几个使用imax()函数的例子,该函数和imin()相似。在程序清单9.4中的程序以旧的形式声明函数imax(),而后错误的使用该函数。操作系统

程序清单9.4 misuse.c程序prototype

/*misuse.c --不正确的使用函数*/
#include <stdio.h>
int imax();  /*旧式的函数声明*/
int main(void)
{
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3));
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3.0,5.0));
    return 0;
}
int imax(n,m)
int n,m;
{
    int max;
    if(n>m)
        max=n;
    else
        max=m;
    return max;
}

在第一个printf()中调用函数imax()时漏掉了一个参数,而在第二次调用imax()时使用了浮点参数而不是整数参数。尽管存在这些错误,该程序仍能够编译执行。code

程序运行时发生了什么?不一样操做系统的内部机制不一样,因此出现错误的具体状况也不相同。当使用PC或VAX时,程序执行过程是这样的:调用函数首先把参数放在一个被称为堆栈(stack)的临时存储区域里,而后被调函数从堆栈中读取这此参数。可是这两个过程并无相互协调进行。调用函数根据调用过程当中实际参数类型肯定须要传递的数值类型,可是被调函数是根据其形式参数的类型进行数据读取的。所以,函数调用 imax(3)把一个整数放在堆栈中。当函数imax()开始执行时,它会从堆栈中读取两个整数。而实际上只有一个须要的数值被存储在堆栈中,因此第二个读取的数据就是当时刚好在堆栈中的其余数值。字符串

第二次使用函数imax()时,传递的是float类型的数值。这时两个double类型的数值就被放在堆栈中(回忆一下,做为参数传递时float类型数据会被转换成double类型数据)。而在咱们使用的系统中,这意味着两个64位的数值,即共128位的数据存储在堆栈中。由于这个系统中int系统是32位,因此当imax()从堆栈中读取两个int类型的数值时,它会读取出堆栈中前面64位的数据,把这些数据对应于两个整数,其中较大的一个就是1074266112。原型

9.2.2  ANSI的解决方案编译器

针对以上的参数错误匹配问题,ANSI标准的解决方案是在函数声明中同时说明所使用的参数类型。即便用函数原型(function prototype)来声明返回值类型、参数个数以及各参数的类型。为了表示imax()须要两个int类型的参数,可使用下面原型中的任意一个进行声明:io

int imax(int ,int);编译

int imax(int a,int b);function

第一种形式使用逗号对参数类型进行分隔;而第二种形式在类型后加入了变量名。须要注意的是这此变量名只是虚拟的名字,它们没必要和函数定义中使用的变量名相匹配。

使用这种函数原型信息,编译器就能够检查函数调用语句是否和其原型声明一致。好比检查参数个数是否正确,参数类型是否匹配。若是有一个参数类型不匹配但都是数值类型,编译器会把实际参数值转换成和形式参数类型相同的数值。例如 ,会把imax(3.0,5.0)换成imax(3,5).

当使用函数原型时,上例中的程序清单9.4会变成以下程序清单9.5。

程序清单9.5  proto.c程序

/*misuse.c --使用函数原型*/
#include <stdio.h>
int imax(int,int);  /*原型*/
int main(void)
{
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3));
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3.0,5.0));
    return 0;
}
int imax(int n,int m)
{
    int max;
    if(n>m)
        max=n;
    else
        max=m;
    return max;
}

当编译程序清单9.5时,编译器会给出一个错误信息,声称调用函数imax()时传递的参数太少。咱们用imax(3,5)代替imax(3)后从新进行编译。这一次并无出现任何错误信息。

虽然编译中没有出现错误信息,可是编译器给出了一条警告信息,提示doube类型数据被转换成了int类型的数据,所以可能会损失数据。例如,如下函数调用:

imax(3.9,5.4);等价于语句imax(3,5);

错误和警告的不一样之处在于前者阻止了编译的继续然后者不阻止。

9.2.3  无参数和不肯定参数

假设使用如下函数原型:

void printf_name();

这时一个ANSI C 编译器会假设您没有用函数原型声明函数,它就不会进行参数检查。所以,为了表示一个函数确实不使用参数,须要在圆括号内加入void关键字:

void printf_name(void);

ANSI C 会把上句解释为pintf_name()不接受任何参数,所以当对函数进行调用时编译器就会检查以保证您确实没有使用参数。一些函数使用的参数个数是变化的。例如,在printf()中,第一个参数是一个字符串,而其他参数的类型以及参数个数并不固定。对于这种状况,ANSI C 容许使用不肯定的函数原型。例如,对于printf()可使用下面的原型声明:

int printf(char *,...);

这种原型表示第一个参数是一个字符串,而其他参数不能肯定。

对于参数个数不肯定的函数,C库经过stdarg.h头文件提供了定义该类函数的标准方法。第16章“C预处理器和C库”详细讲述了有关内容。

9.2.4  函数原型的优势

函数原型是对语言的有力补充。它可使编译器发现函数使用时可能出现的错误或疏漏

有一种方法能够不使用函数原型却保留函数原型的优势。之因此使用函数原型,是为了在编译器编译第一个调用函数的语句以前向其代表该函数的使用方法。所以,能够在首次调用某函数以前对该函数进行完整的定义。这样函数定义部分就和函数原型有着相同的做用。一般对于较小的函数会这样作:

//下面便是一个函数定义,也是它的原型

int imax(int a,int b)  { return a>b ? a:b;}

int main(void)

{

...

z=imax(x,50);

...

}

相关文章
相关标签/搜索