scanf() 是 C 語言標準庫中最核心的格式化輸入函數,雖然用法看似簡單,但其格式字串細節、地址傳遞機制、輸入緩衝區特性、錯誤處理機制等深層機制,是無數初學者甚至資深開發者頻繁踩坑的重災區。本文將從原理、語法、陷阱、最佳實踐四個維度,徹底講透 scanf()。
scanf() 的本質是「向變量所在內存地址寫入數據」。這與賦值語句 a = 5(直接操作變量名)有本質區別:
| 操作 | 語法 | 本質 | 左值要求 |
|---|---|---|---|
| 賦值語句 | a = 5; | 編譯器知道 a 的地址,直接寫入 | 變量名 |
| scanf 輸入 | scanf("%d", &a); | 程式員必須顯式提供地址 &a | 地址表達式 |
& 是取地址運算符,&a 是一個表達式,其值為變量 a 在內存中的起始地址。
int scanf(const char *format, ...);
// 返回值:成功讀取的項目數;EOF 表示輸入失敗/結束
一般形式:
scanf("格式控制字串", 地址表列);
%d),不能包含提示文字(這點與 printf 不同)&(字符數組/指針除外)%[*][寬度][長度]類型
| 格式符 | 對應類型 | 說明 | |
|---|---|---|---|
%d | int * | 十進制整數 | |
%ld | long * | 長整數 | |
%lld | long long * | 長長整數 (C99) | |
%f/%lf | float */double * | 浮點數(scanf 必須區分 %f/%lf) | |
%c | char * | 單個字符(含空白字符) | |
%s | char * | 字符串(遇空白結束) | |
%o/%x | unsigned * | 八/十六進制整數 | |
%u | unsigned * | 無符號十進制 |
| 修飾符 | 位置 | 作用 | 範例 |
|---|---|---|---|
* | % 後 | 抑制賦值:讀取但不存儲,跳過該輸入 | %*d 跳過一個整數 |
| 寬度 | * 後 | 最大讀取字符數 | %5d 最多讀 5 位數字 |
h | 類型前 | short 整數 | %hd |
l | 類型前 | long 整數 / double 浮點數 | %ld, %lf |
ll | 類型前 | long long (C99) | %lld |
L | 類型前 | long double | %Lf |
| 陷阱 | 症狀 | 解決 |
|---|---|---|
| 漏寫 & | 崩潰/亂碼 | 所有非數組/指針變量必加 & |
| %f/%lf 混用 | 浮點數讀錯 | float 用 %f,double 用 %lf |
| %c 讀到空白 | 字符錯位 | 格式串加空格 " %c" 或用 fgets |
| 緩衝區殘留換行 | 下次 %c 讀空 | 加空格/讀走/改用 fgets+sscanf |
| 不檢查返回值 | 錯誤輸入導致變量未初始化 | 必檢查返回值 == 預期項目數 |
| 輸入格式不匹配 | 掃描卡住/停止 | 格式串匹配預期輸入格式 |
scanf() 是 C 語言輸入的基石,其地址傳遞語義、格式掃描機制、緩衝區特性構成了完整的輸入模型。掌握其精髓,不僅能避開無數坑,更能深入理解 C 語言「內存、地址、指針」的核心哲學。生產環境建議採用 fgets() + sscanf() 組合,將輸入解析與緩衝管理分離,構建更健壯的輸入系統。