函數原型
#include <stdio.h>
int scanf(const char *format, ...);
功能:按格式字符串從標準輸入讀取數據,存入指定變量地址。返回成功賦值的參數個數,失敗返回 EOF。
格式控制字符串完整構成
% [*] [寬度] [長度修飾符] 類型字符
1. 類型字符對照表
| 字符 | 含義 |
| %d / %i | 十進制整數(%i 兼容八/十六進制) |
| %o | 八進制整數 |
| %x / %X | 十六進制整數 |
| %u | 無符號十進制整數 |
| %c | 單個字符 |
| %s | 字符串(遇空白結束) |
| %f / %F / %e / %E / %g / %G | 浮點數 |
| %p | 指針地址 |
| %n | 已讀字符數寫入對應 int* 參數 |
| %[...] | 掃描字符集合(如 %[^\n] 讀整行) |
| %% | 匹配字面 % |
2. 長度修飾符
| 修飾符 | 適用類型 | 含義 |
| h | d,i,o,x,u | short int / unsigned short |
| hh | d,i,o,x,u | signed char / unsigned char (C99) |
| l | d,i,o,x,u | long int / unsigned long |
| l | f,e,g | double(printf 中 %lf 同 %f) |
| ll | d,i,o,x,u | long long (C99) |
| L | f,e,g | long double |
3. 寬度與 * 抑制賦值
%10d:最多讀 10 字符作為整數
%*d:讀取但不賦值(跳過該輸入)
參數必須傳地址
| 變量類型 | 傳參寫法 |
| 普通變量 | &var |
| 陣列/字符串 | arr 或 &arr[0] |
| 指針 | ptr(本身即地址) |
返回值與錯誤檢測
- 返回 成功賦值的參數個數
- 輸入失敗/匹配失敗 → 返回
EOF,可用 ferror(stdin) 檢測
- 遇到文件結束符 → 返回
EOF,可用 feof(stdin) 檢測
- 寬字符編碼錯誤 → 設
errno = EILSEQ
經典實例
1. 基本讀取
char name[20];
int age;
printf("Enter your family name: ");
scanf("%s", name);
printf("Enter your age: ");
scanf("%d", &age);
printf("Mr. %s, %d years old.\n", name, age);
2. 讀取十六進制
unsigned int hex;
printf("Enter a hexadecimal number: ");
scanf("%x", &hex);
printf("You have entered 0x%x (%d).\n", hex, hex);
3. 讀取含空格的整行字符串
char str[80];
scanf("%[^\n]", str); // 遇換行符停止
printf("%s\n", str);
進階技巧與避坑指南
- 清理輸入緩衝區:
scanf("%*[^\n]"); scanf("%*c"); 跳過殘留換行符
- 防止緩衝區溢出:字符串輸入指定寬度
%79s
- %n 安全風險:格式化字符串攻擊利用點,現代編譯器默認禁用或需顯式啟用
- 混合輸入陷阱:
scanf("%d") 後殘留 \n 導致後續 %c 或 %[^\n] 直接讀到空行,需額外消耗換行符
總結
scanf 是 C 語言格式化輸入的核心,掌握格式字符、修飾符、地址傳遞、返回值檢測與緩衝區處理,能避開 90% 的輸入相關 Bug。現代代碼建議改用 fgets + sscanf 分離讀取與解析,更安全可控。