细心的同学从上一章节的函数定义可以看出来,函数的返回值并非只有 void 或 int 型,所以本章节我们就看下返回非整型值的函数是怎么做的。
第四章 函数与程序结构 >> 4.2 返回非整型值的函数
1. 返回值为 double 的 atof() 函数
来看个 atof(s) 函数,该函数用于把字符串 s 转换为相应的双精度浮点数。(标准库 stdlib.h 中包含了类似功能的 atof() 函数)
#include <stdio.h> #include <ctype.h> #define MAXLINE 100 double atof(char []); int my_getline(char line[], int max); /* rudimentary calculator */ int main() { double sum; char line[MAXLINE]; sum = 0; while (my_getline(line, MAXLINE) > 0) printf("\t%g\n", sum += atof(line)); return 0; } /* atof: convert string s to double */ double atof(char s[]) { double val, power; int i, sign; for (i = 0; isspace(s[i]); i++) /* skip white space */ ; 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; } return sign * val / power; } /* my_getline: get line into s, return length */ int my_getline(char s[], int lim) { int c, i; i = 0; while (--lim > 0 && (c=getchar()) != EOF && c != '\n') s[i++] = c; if (c == '\n') s[i++] = c; s[i]= '\0'; return i; }
显然 atof() 函数的返回值应当是 double 型,因此,我们在程序的开头位置就显式的声明了函数的返回值是 double,且有一个 char [ ] 类型的参数。而在 atof() 函数定义的地方,我们也是与函数声明保持了相同的返回值类型和参数类型。
需要特别说明的是:atof() 函数的声明和定义必须一致。这是因为:
- 如果 atof() 函数和调用它的主函数 main() 在同一个源文件中编译,并且声明与定义的类型不一致,则编译器能检测到该错误;
- 如果 atof() 函数是单独编译,那这种函数声明与定义不匹配的错误,就无法被检测出来。这种情况下,atof() 返回 double 结果,而 main() 却将返回值当成 int 型处理,最后的结果将毫无意义。
实际编程中,恰恰是第 2 种情况更容易发生……
2. 参数为空的函数声明
如果函数声明中不包含参数,例如:
double atof();
则编译器不会对 atof() 函数的参数做任何假设,同时关闭所有的参数检查。对空参数列表的这种特殊处理是为了使新的编译器能编译比较老的C语言程序。不过在新的C语言程序中,我们提倡的是:如果函数带参数,则要声明它们;如果不带参数,则使用 void 声明。
3. 基于 atof() 的 atoi() 函数
在正确声明的函数 atof 基础上,我们可以利用它快速编写函数 atoi (将字符串转化为 int 类型):
/* atoi: convert string s to integer using atof */ int atoi(char s[]) { double atof(char s[]); return (int) atof(s); }
其中 atof(s) 的值在返回前,进行了强制类型转换,转换成了 int 类型(和 atoi 函数的返回值类型一致)。
如果把上面的 return 语句改成:
return atof(s);
那么由于 atoi 函数的返回值类型是 int 型,因此 atof(s) 的返回值 double 类型会被自动转换为 int 类型值,这种操作可能会丢失信息。某些编译器可能会对此给出警告信息。
(然后前者由于显式的进行了类型转换,明确所要进行的转换操作,因此不会出现警告信息)