華氏溫度轉攝氏溫度公式:C = 5 × (F - 32) / 9。看似簡單的程式,卻藏著 **scanf 格式字串陷阱**與**整數除法精度丟失**兩大經典坑點。
#include <stdio.h>
int main() {
double c, f;
printf("請輸入華氏溫度: ");
scanf("%lf\n", &f); // 錯誤:格式字串含 \n
c = 5 * (f - 32) / 9;
printf("f=%lf\nc=%lf\n", f, c);
return 0;
}
現象:程式「卡住」,必須輸入多行非空白字元才會繼續。
原因:scanf 格式字串中的空白字元(含 \n、空格、Tab)意味著**「讀取並丟棄任意數量的空白字元,直到遇到非空白字元」**。輸入數字後按 Enter,\n 讓 scanf 繼續等待下一個非空白字元,導致看似死循環。
#include <stdio.h>
int main() {
double c, f;
printf("請輸入華氏溫度: ");
scanf("%lf", &f); // 正確:無尾隨空白
c = 5 * (f - 32) / 9.0; // 關鍵:9.0 強制浮點除法
printf("f=%lf\nc=%lf\n", f, c);
return 0;
}
c = 5 * (f - 32) / 9; // 錯誤:5、9 為 int,整數除法 c = 5 * (f - 32) / 9.0; // 正確:9.0 為 double,浮點除法
雖然 f 是 double,但 5 * (f - 32) 結果為 double,除以 9 (int) 會隱式轉換為 double,**實際上此處不會整數除法**。但若寫成 5 / 9 * (f - 32),5/9 先執行整數除法得 0,結果全錯。**最佳實踐:常數參與浮點運算時全部加 .0**。
| 格式字串 | 行為 | 建議 |
|---|---|---|
"%d" | 跳過前導空白,讀取整數,停留在下一字元 | 標準用法 |
"%d\n" | 讀取整數後,繼續吞噬後續所有空白(含換行),等待非空白 | **極少使用**,易卡住 |
" %d" | 前導空白強制跳過(%d 本身就會跳過),效果相同 | 冗餘但無害 |
"%c" | **不跳過空白**,直接讀取下一字元(含空格、換行) | 需配合 " %c" 跳過空白 |
double read_double(const char *prompt) {
double val;
int ret;
while (1) {
printf("%s", prompt);
ret = scanf("%lf", &val);
if (ret == 1) break; // 成功讀取
if (ret == EOF) exit(1); // 檔案結束
// ret == 0:輸入不匹配,清空緩衝區
scanf("%*[^\n]"); // 丟棄至換行
scanf("%*c"); // 丟棄換行
puts("輸入無效,請重新輸入數字。");
}
return val;
}
\n),除非你確定要「吞噬後續所有空白」。