ansi c标准_C语言的ANSI-C函数原型的两个问题

0961a4eb607e4d70f41700148864329c.png

在ANSI-C标准之前,声明函数的方案有缺陷,因为只需要声明函数的类型,不用声明任何参数。下面我们看一下使用旧式的函数声明会导致什么问题。下面是ANSI之前的函数声明,告知编译器imin()返回int类型的值:

int imin();

然而,以上函数声明并未给出imin()函数的参数个数和类型。因此,如果调用imin()时使用的参数个数不对或类型不匹配,编译器根本不会察觉出来。

1 问题所在

我们看看与imax()函数相关的一些示例,该函数与imin()函数关系密切。程序清单9.4演示了一个程序,用过去声明函数的方式声明了imax()函数,然后错误地使用该函数。

/* misuse.c -- uses a function incorrectly */#include int imax();      /* old-style declaration */int main(void){    printf("The maximum of %d and %d is %d.",            3, 5, imax(3));    printf("The maximum of %d and %d is %d.",            3, 5, imax(3.0, 5.0));    return 0;}int imax(n, m)int n, m;{    return (n > m ? n : m);}

第1次调用printf()时省略了imax()的一个参数,第2次调用printf()时用两个浮点参数而不是整数参数。尽管有些问题,但程序可以编译和运行。

输出示例:

: The maximum of 3 and 5 is 1590788712.: The maximum of 3 and 5 is 187311296.

使用gcc运行该程序,输出的值是1590788712和187311296。这两个编译器都运行正常,之所以输出错误的结果,是因为它们运行的程序没有使用函数原型。

到底是哪里出了问题?由于不同系统的内部机制不同,所以出现问题的具体情况也不同。下面介绍的是使用PC和VAX的情况。主调函数把它的参数存储在被称为栈(stack)的临时存储区,被调函数从栈中读取这些参数。对于该例,这两个过程并未相互协调。主调函数根据函数调用中的实际参数决定传递的类型,而被调函数根据它的形式参数读取值。因此,函数调用imax(3)把一个整数放在栈中。当imax()函数开始执行时,它从栈中读取两个整数。而实际上栈中只存放了一个待读取的整数,所以读取的第2个值是当时恰好在栈中的其他值。

第2次使用imax()函数时,它传递的是float类型的值。这次把两个double类型的值放在栈中(回忆一下,当float类型被作为参数传递时会被升级为double类型)。在我们的系统中,两个double类型的值就是两个64位的值,所以128位的数据被放在栈中。当imax()从栈中读取两个int类型的值时,它从栈中读取前64位。在我们的系统中,每个int类型的变量占用32位。这些数据对应两个整数,其中较大的是187311296。

2 ANSI的解决方案

针对参数不匹配的问题,ANSI-C标准要求在函数声明时还要声明变量的类型,即使用函数原型(function-prototype)来声明函数的返回类型、参数的数量和每个参数的类型。未标明imax()函数有两个int类型的参数,可以使用下面两种函数原型来声明:

int imax(int, int);int imax(int a, int b);

第1种形式使用以逗号分隔的类型列表,第2种形式在类型后面添加了变量名。注意,这里的变量名是假名,不必与函数定义的形式参数名一致。

有了这些信息,编译器可以检查函数调用是否与函数原型匹配。参数的数量是否正确?参数的类型是否匹配?以imax()为例,如果两个参数都是数字,但是类型不匹配,编译器会把实际参数的类型转换成形式参数的类型。例如,imax(3.0, 5.0)会被转换成imax(3, 5)。我们用函数原型替换程序清单9.4中的函数声明,如下面程序所示。

/* proto.c -- uses a function prototype */#include int imax(int, int);        /* prototype */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){    return (n > m ? n : m);}

编译上述程序,我们的编译器给出调用的imax()函数参数太少的错误消息。

如果是类型不匹配会怎样?为探索这个问题,我们用imax(3,5)替换imax(3),然后再次编译该程序。这次编译器没有给出任何错误信息,程序的输出如下:

The maximum of 3 and 5 is 5.The maximum of 3 and 5 is 5.

如上文所述,第2次调用中的3.0和5.0被转换成3和5,以便函数能正确地处理输入。

虽然没有错误消息,但是我们的编译器还是给出了警告:double转换成int可能会导致丢失数据。例如,下面的函数调用:

imax(3.9, 5.4)

相当于:

imax(3, 5)

错误和警告的区别是:错误导致无法编译,而警告仍然允许编译。一些编译器在进行类似的类型转换时不会通知用户,因为C标准中对此未作要求。不过,许多编译器都允许用户选择警告级别来控制编译器在描述警告时的详细程度。

3 无参数和未指定参数

假设有下面的函数原型:

void print_name();

一个不支持ANSI-C的编译器会假定用户没有用函数原型来声明函数,它将不会检查参数。为了表明函数确实没有参数,应该在圆括号中使用void关键字:

void print_name(void);

支持ANSI-C的编译器解释为print_name()不接受任何参数。然后在调用该函数时,编译器会检查以确保没有使用参数。

一些函数接受(如,printf()和scanf())许多参数。例如对于printf(),第1个参数是字符串,但是其余参数的类型和数量都不固定。对于这种情况,ANSI C允许使用部分原型。例如,对于printf()可以使用下面的原型:

int printf(const char *, ...);

这种原型表明,第1个参数是一个字符串(第11章中将详细介绍),可能还有其他未指定的参数。

C库通过stdarg.h头文件提供了一个定义这类(形参数量不固定的)函数的标准方法。第16章中详细介绍相关内容。

4 函数原型的优点

函数原型是C语言的一个强有力的工具,它让编译器捕获在使用函数时可能出现的许多错误或疏漏。如果编译器没有发现这些问题,就很难觉察出来。是否必须使用函数原型?不一定。你也可以使用旧式的函数声明(即不用声明任何形参),但是这样做的弊大于利。

有一种方法可以省略函数原型却保留函数原型的优点。首先要明白,之所以使用函数原型,是为了让编译器在第1次执行到该函数之前就知道如何使用它。因此,把整个函数定义放在第1次调用该函数之前,也有相同的效果。此时,函数定义也相当于函数原型。对于较小的函数,这种用法很普遍:

// the following is a definition and a prototypeint imax(int a, int b) { return a > b ? a : b; }int main(){    int x, z;...    z = imax(x, 50);...}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值