我們嘗試用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
C 風格讀取循環
根據偽代碼的設計思路,程序清單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的語法特性讓你可以用下面的精簡版本替換標準版本。