初學者常在 scanf/printf、緩衝區清理、goto 迴圈三個環節踩坑。本文逐一拆解「位址誤當值輸出」、「clear_kb 實作缺陷」、「goto 導致 scanf 被跳過」三大經典問題,並給出標準修正寫法。
int main() {
int ir = 0;
uur:
printf("333\n");
scanf("%d", &ir);
clear_kb();
printf("t=%d\n", ir); // 正確:印出輸入值
printf("t=%d\n", &ir); // 錯誤:印出位址,且用 %d 解讀
goto uur;
}
void clear_kb(void) {
char junk[4096];
// gets(junk); // 危險且已棄用
}
現象:printf("t=%d\n", &ir); 輸出負數(-58、-60 等),實為堆疊位址的高位元被視為負號。
原因:&ir 是 int* 型別,需用 %p 配合 (void*) 列印。
修正:
printf("ir value = %d\n", ir); // 印值
printf("ir address = %p\n", (void*)&ir); // 印位址
現象:輸入非數字字元後,scanf 失敗,殘留字元導致下一輪 scanf 直接讀取舊字元而非等待輸入。
標準清理模式:
void clear_kb(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) { /* discard */ }
}
進階:搭配 scanf 回傳值檢查
int ret = scanf("%d", &ir);
if (ret != 1) { // 輸入非整數
clear_kb(); // 清理錯誤字元
continue; // 重新提示
}
現象:第二次迴圈 printf("333\n") 執行,但 scanf 似乎未等待輸入直接跑完。
真相:非 goto 問題,而是上一輪 scanf 失敗(如輸入 'a'),殘留 'a' 與 '\n' 留在 stdin,下一輪 scanf 立即讀取失敗返回,程式繼續跑,看起來像「跳過」。
根治:檢查 scanf 回傳值 + 失敗時清理緩衝區,而非依賴 goto。
#include
#include
void clear_stdin(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main(void) {
int ir;
while (1) {
printf("請輸入整數 (Ctrl+Z 結束): ");
int ret = scanf("%d", &ir);
if (ret == EOF) break; // 使用者送出 EOF
if (ret != 1) { // 輸入非整數
printf("輸入無效,請重新輸入。\n");
clear_stdin();
continue;
}
printf("您輸入的值 = %d\n", ir);
printf("變數位址 = %p\n", (void*)&ir);
}
return 0;
}
| 錯誤模式 | 正確做法 |
|---|---|
printf("%d", &var) | printf("%p", (void*)&var) |
忽略 scanf 回傳值 | 必檢查 ret == 1 (或預期欄位數) |
用 gets/fgets 清理 stdin | 用 getchar() 迴圈丟棄至換行/EOF |
用 goto 構建主迴圈 | 用 while(1) + break/continue |
main() 省略回傳型別 | 標準寫法 int main(void) 並 return 0; |
scanf/printf 看似簡單,實則暗藏型別、緩衝區、回傳值三大陷阱。養成「檢查回傳值、正確格式化字串、標準化緩衝區清理」三習慣,並以 while 取代 goto,即能寫出健壯、可維護的互動式主控台程式。