4.2 返回非整数的函数
到目前为止,我们给出的函数样例,不是不返回值(void)就是返回 int。如果函数必须要返回其他类型呢?许多的数值函数如 sqrt,sin 和 cos 返回 double;其他特定的函数返回其他类型的值。为了说明该如何处理这种情况,我们来编写并使用函数 atof(s),它将字符串 s 转换成对应的双精度浮点数。 atof 是 我们在第二章和第三章所展示的 atoi 函数的扩展。它能处理可选的正负号和小数点,以及可能缺失的整数或小数部分。我们即将给出的版本不是一个高质量的输入转换例程,因为我们不想占用更多篇幅。标准库中包含了一个 atof,在头文件 <stdlib.h>中声明。
首先,因为不返回int,故atof 必须声明它的返回值。返回类型放在函数名称的前面:
c
#include <ctype.h>
/* atof: 将字符串s转换成double */
double atof(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); i++) /* 跳过空格 */
;
sign = s[i] == '-' ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i+) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
return sign * val / power;
}
其次,但是也同样重要的,调用者必须知道 atof 返回非int值。保证这一点的一种办法是在调用者函数内显式声明 atof。这种方法展示在下面这个简单的计算器中(勉强够用来做账单结算)。计算器每行读取一个可能带符号的数,将它们加起来,并在每次输入后打印当前的和。
c
#include <stdio.h>
#define MAXLINE 100
/* 简单的计算器 */
main()
{
double sum, atof(char []);
char line[MAXLINE];
int getline(char line[], int max);
sum = 0;
while (getline(line, MAXLINE) > 0)
printf("\t%g\n", sum += atof(line));
return 0;
}
其中的声明
c
double sum, atof(char []);
表示sum 是一个double变量,而 atof 是一个函数,它有一个 char[] 参数,并返回double。
函数 atof 的声明和定义必须一致。如果在同一个源文件内, atof 自身定义的类型与 main 调用它时声明的类型不一致,则编译器能检测到这个错误。但如果(更有可能)atof 是分开编译的,无法检测到两者不匹配,atof 返回 double 而 main 将它当作 int,就会得到无意义的结果。
鉴于我们说过声明必须与定义匹配,这个说法可能令人吃惊。导致不匹配发生的原因在于,如果没有函数原型,则该函数第一次在表达式中出现时,它会被隐式声明,比如
c
sum += atof(line)
如果一个没有被事先声明的名称出现在表达式中,而且名字后面还跟着左括号,则它被根据上下文声明为一个函数名称,该函数被假定返回int,且对其参数不做任何假定。更进一步,如果函数声明没有包括参数,例如
c
double atof();
此时同样也不会对 atof 的参数做任何假定;所有参数校验都会被关闭。对空参数列表的这种特殊处理,是为了让新编译器能够编译旧的C程序。但新写的程序如果还这么做,会是非常糟糕的。如果函数有参数,就要声明出来;如果没有参数,使用void。
有了正确声明的atof,我们就能用它来写atoi(把字符串转换成int):
c
/* atoi: 使用atof将字符串s转换成整数 */
int atoi(char s[])
{
double atof(char s[]);
return (int) atof(s);
}
注意其中的声明结构和 return 语句。
c
return 表达式;
在返回发生之前,其中表达式的值会被转换成函数的类型。因此,当出现在这个 return 里面时,atof 的值 ,一个 double,会自动被转换成 int,因为 atoi 函数返回一个 int。然而,这个操作会潜在地丢失信息,因此一些编译器会给出警告。强制类型转换显式地告知编译器,这个操作是有意的,从而抑制了告警。
练习 4-2:扩展 atof,使之能处理科学计数形式
c
123.45e-6
其中的浮点数后面可以跟着 e 或 E,以及可选的带符号的幂。