我们尝试用scanf做累加运算:
/* summing.c -- sums integers entered interactively */
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L; /* initialize sum to zero */
int status;
printf("Please enter an integer to be summed ");
printf("(q to quit): ");
status = scanf("%ld", &num);
while (status == 1) /* == means "is equal to" */
{
sum = sum + num;
printf("Please enter next integer (q to quit): ");
status = scanf("%ld", &num);
}
printf("nThose integers sum to %ld.n", sum);
return 0;
}
该程序使用long类型以存储更大的整数。尽管C编译器会把0自动转换为合适的类型,但是为了保持程序的一致性,我们把sum初始化为0L(long类型的0),而不是0(int类型的0)。该程序的运行示例如下:
Please enter an integer to be summed (q to quit): 44
Please enter next integer (q to quit): 33
Please enter next integer (q to quit): 88
Please enter next integer (q to quit): 121
Please enter next integer (q to quit): q
Those integers sum to 286.
先看while循环,该循环的测试条件是如下表达式:
status == 1
运算符是C的相等运算符(equality-operator),该表达式判断status是否等于1。不要把status == 1 与 status = 1混淆,后者是把1赋给status。根据测试条件status == 1,只要status等于1,循环就会重复。每次循环,num的当前值都被加到sum上,这样sum的值始终是当前整数之和。当status的值不为1时,循环结束。然后程序打印sum的最终值。
要让程序正常运行,每次循环都要获取num的一个新值,并重置status。程序利用scanf()的两个不同的特性来完成。首先,使用scanf()读取num的一个新值;然后,检查scanf()的返回值判断是否成功获取值。第4章中介绍过,scanf()返回成功读取项的数量。如果scanf()成功读取一个整数,就把该数存入num并返回1,随后返回值将被赋给status(注意,用户输入的值存储在num中,不是status中)。
这样做同时更新了num和status的值,while循环进入下一次迭代。如果用户输入的不是数字(如,q),scanf()会读取失败并返回0。此时,status的值就是0,循环结束。因为输入的字符q不是数字,所以它会被放回输入队列中(实际上,不仅仅是q,任何非数值的数据都会导致循环终止,但是提示用户输入q退出程序比提示用户输入一个非数字字符要简单)。
如果scanf()在转换值之前出了问题(例如,检测到文件结尾或遇到硬件问题),会返回一个特殊值EOF(其值通常被定义为-1)。这个值也会引起循环终止。如何告诉循环何时停止?该程序利用scanf()的双重特性避免了在循环中交互输入时的这个棘手的问题。例如,假设scanf()没有返回值,那么每次循环只会改变num的值。虽然可以使用num的值来结束循环,比如把num > 0(num大于0)或num != 0(num不等于0)作为测试条件,但是这样用户就不能输入某些值,如-3或0。也可以在循环中添加代码,例如每次循环时询问用户“是否继续循环?<y/n>”,然后判断用户是否输入y。这个方法有些笨拙,而且还减慢了输入的速度。使用scanf()的返回值,轻松地避免了这些问题。
现在,我们来看看该程序的结构。总结如下:
initialize sum to 0
prompt user
read input
while the input is an integer,
add the input to sum,
prompt user,
then read next input
after input completes, print sum
顺带一提,这叫作伪代码(pseudocode),是一种用简单的句子表示程序思路的方法,它与计算机语言的形式相对应。伪代码有助于设计程序的逻辑。确定程序的逻辑无误之后,再把伪代码翻译成实际的编程代码。使用伪代码的好处之一是,可以把注意力集中在程序的组织和逻辑上,不用在设计程序时还要分心如何用编程语言来表达自己的想法。例如,可以用缩进来代表一块代码,不用考虑C的语法要用花括号把这部分代码括起来。
总之,因为while循环是入口条件循环,程序在进入循环体之前必须获取输入的数据并检查status的值,所以在while前面要有一个scanf()。要让循环继续执行,在循环内需要一个读取数据的语句,这样程序才能获取下一个status的值,所以在while循环末尾还要有一个scanf(),它为下一次迭代做好了准备。可以把下面的伪代码作为while循环的标准格式:
get first value to be tested
while the test is successful
process value
get next value
根据伪代码的设计思路,程序清单6.1可以用Pascal、BASIC或FORTRAN来编写。但是C更为简洁,下面的代码:
status = scanf("%ld", &num);
while (status == 1)
{
/* loop actions */
status = scanf("%ld", &num);
}
// can be replaced by the following:
while (scanf("%ld", &num) == 1)
{
/* loop actions */
}
第二种形式同时使用scanf()的两种不同的特性。
首先,如果函数调用成功,scanf()会把一个值存入num。
然后,利用scanf()的返回值(0或1,不是num的值)控制while循环。
因为每次迭代都会判断循环的条件,所以每次迭代都要调用scanf()读取新的num值来做判断。换句话说,C的语法特性让你可以用下面的精简版本替换标准版本。