文档章节

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

idreamo
 idreamo
发布于 2016/07/10 07:31
字数 1853
阅读 59
收藏 1

9.2.1 产生的问题

下面我们讨论几个使用imax()函数的例子,该函数和imin()类似。在程序清单9.4中的程序以旧的形式声明函数imax(),然后错误的使用该函数。

程序清单9.4 misuse.c程序

/*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()时使用了浮点参数而不是整数参数。尽管存在这些错误,该程序仍可以编译执行。

程序运行时发生了什么?不同操作系统的内部机制不同,所以出现错误的具体情况也不相同。当使用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类型的参数,可以使用下面原型中的任意一个进行声明:

int imax(int ,int);

int imax(int a,int b);

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

使用这种函数原型信息,编译器就可以检查函数调用语句是否和其原型声明一致。比如检查参数个数是否正确,参数类型是否匹配。如果有一个参数类型不匹配但都是数值类型,编译器会把实际参数值转换成和形式参数类型相同的数值。例如 ,会把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);

...

}

 

© 著作权归作者所有

idreamo
粉丝 17
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
C Primer Plus 第9章 函数 9.1 函数概述

9.1 函数概述 首先,什么是函数?函数(funcation)是用于完成特定任务的程序代码的自包含单元。 为什么使用函数?第一,函数的使用可以省去重复代码的编写。第二,即使某种功能在程序中只使用...

idreamo
2016/07/08
41
0
你C语言的无参函数声明写对了吗?

笔者最近在复习C语言函数时遇到了一个自己在写函数声明时时常会犯的一个错误,所以现在想把它写出来跟大家分享一下。 笔者之前在写没有参数的函数的声明时,通常会将void省略,如下: void s...

简一
2016/03/02
947
0
C Primer Plus 第11章 11.9 把字符串转换为数字

数字即能以字符串形式也能 以数字形式存储。以字符串形式存储数字就是存储数字字符。例如,数字213即能以数字'2'、‘1’、‘3’、‘0’的形式存储在一个字符串数组中。以数字形式存储213意味...

idreamo
2016/08/29
28
0
C Primer Plus 第12章 12.7 ANSI C的类型限定词

您已经知道一个变量是以它的类型与存储类表征的。C90增加了两个属性:不变性和易变性。这些属性是通过关键字const和volatile声明的,这样就创建了受限类型(qualified type)。C99标准添加了第...

idreamo
2016/12/28
4
0
C Primer Plus 第12章 12.4 随机数函数和静态变量

首先,来看一个随机数函数,该函数使用了一个具有内部链接的静态变量。ANSI C程序库提供了rand()函数来产生随机数。有多种随机数的算法,ANSI C 标准允许C实现使用针对特定机器的最佳算法。不...

idreamo
2016/11/24
22
0

没有更多内容

加载失败,请刷新页面

加载更多

浅谈 Spark 的多语言支持

作者:郑锴,花名铁杰,阿里巴巴高级技术专家,Apache Hadoop PMC,Apache Kerby 创立者。深耕分布式系统开发和开源大数据多年,先后专注在安全,存储和计算领域。之前在 Intel,目前转战阿里...

阿里云云栖社区
21分钟前
2
0
Linux运维常见的硬件及系统问题

一、服务器常见故障和现象 1、有关服务器无法启动的主要原因 : ①市电或电源线故障(断电或接触不良) ②电源或电源模组故障 ③内存故障(一般伴有报警声) ④CPU故障(一般也会有报警声) ⑤主板故...

寰宇01
28分钟前
0
0
Confluence 6 针对 'unmigrated-wiki-markup' 宏重新尝试合并

在签名的章节中,我们主要是针对没有完全合并完成余下的为合并内容的异常处理。最常见的情况是内容以及被合并了,但是页面使用 wiki 标记的内容没有被合并,通常这些 wiki 标记的内容使用了 ...

honeymoose
39分钟前
2
0
ubuntu 18.04桌面版启动错误: Unable to mount root fs on unknown-block(0,0)

问题出现过程 *** 我的环境: visualbox 虚拟机 Ubuntu 18.04 桌面版 IP:192.168.1.186 (最初从 192.168.1.185 克隆过来的) 20190423 晚上准备安装一个 UNlet standalone 和 一个 Eclips...

wwzzhh166
40分钟前
1
0
fescar select for update 读隔离级别实现

/* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in complian......

xiaomin0322
43分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部