文档章节

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

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

精选30+云产品,助力企业轻松上云!>>>

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
粉丝 18
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
加载中
请先登录后再评论。
C Primer Plus 第9章 函数 9.1 函数概述

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

idreamo
2016/07/08
77
0
【STM32H7教程】第9章 STM32H7重要知识点数据类型,变量和堆栈

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第9章 STM32H7重要知识点数据类型,变量和堆栈 本章教程为大家介绍数据类型,变量和堆栈的相关知识。 9.1 初学...

osc_ertc0ko2
2019/04/22
1
0
C Primer Plus第6版_源代码+练习答案

下载地址:网盘下载 C Primer Plus(第6版)中文版详细讲解了C语言的基本概念和编程技巧。《C Primer Plus(第6版)中文版》共17章。第1、2章介绍了C语言编程的预备知识。第3~15章详细讲解了...

osc_d9817zy2
2018/07/06
14
0
你C语言的无参函数声明写对了吗?

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

简一
2016/03/02
1.6K
0
C Primer Plus (第6版) 读书笔记_Chapter 2

本章介绍以下内容: ■ 运算符:= ■ 函数:main()、printf() ■ 编写一个简单的 C 程序 ■ 创建整型变量,为其赋值并在屏幕上显示其值 ■ 换行符 ■ 如何在程序中写注释,创建包含多个函数的...

osc_d6r1n9ox
2018/06/07
4
0

没有更多内容

加载失败,请刷新页面

加载更多

521我发誓读完本文,再也不会担心Spring配置类问题了

当大潮退去,才知道谁在裸泳。关注公众号【BAT的乌托邦】开启专栏式学习,拒绝浅尝辄止。本文 https://www.yourbatman.cn 已收录,里面一并有Spring技术栈、MyBatis、中间件等小而美的专栏供...

osc_72k9vb4y
16分钟前
8
0
微服务思考(01):什么是微服务?微服务的优势和劣势

一、单体应用 在软件开发早期阶段,大家都在一个应用系统上开发。各个业务模块之间耦合也比较紧密。软件发布也是整体发布,或者对软件进行打包发布和部署,比如java可以打包成war部署。测试也...

osc_bcvwusz2
17分钟前
9
0
Halcon一维测量1D Measuring解析

一维测量(也叫一维计量或卡尺)的概念非常直观。沿着一个预定的区域(主要是垂直于RIO感兴趣区域的方向) 边缘的位置。这里的边缘为从暗到亮或从亮到暗的过渡。 基于提取的边缘,可以测量零件...

osc_xs2d5ls9
18分钟前
10
0
U盘+grub2安装centos8实战

1. U盘准备 这里的U盘也可以换成硬盘 grub2安装一直失败,怀疑U盘坏了,下面命令修复了一下 [root@host2 ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda ...

osc_43xj61td
19分钟前
7
0
遇见SQL(2)

SQL自学笔记 约束 1.0概念及分类 2.0非空约束 3.0 唯一约束 4.0 主键约束 自动增长 5.0 外键约束 级联操作 多表关系 三种情况概述 数据库的备份和还原 多表查询 1.0 内连接查询 隐式内连...

osc_4eht81t7
20分钟前
19
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部