既然 printf 族函數可通過可變參實現,scanf 族同理。本文展示基於 vscanf 的 myscanf 封裝實現,揭示輸入函數可變參轉發的核心機制。
核心代碼
#include
#include
int myscanf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
// vscanf 是 scanf 的可變參版本,接收 va_list
int ret = vscanf(fmt, ap);
va_end(ap);
return ret;
}
int main() {
int num;
printf("pls input num:\n");
myscanf("%d", &num); // 透傳格式字符串與地址參數
printf("num: %d\n", num);
return 0;
}
運行演示
pls input num:
100
num: 100
關鍵機制解析
| 函數 | 參數類型 | 說明 |
scanf | (const char*, ...) | 標準掃描,可變參 |
vscanf | (const char*, va_list) | 接收預打包的 va_list,核心實現 |
fscanf | (FILE*, const char*, ...) | 文件流掃描 |
vfscanf | (FILE*, const char*, va_list) | 文件流可變參核心 |
sscanf | (const char*, const char*, ...) | 字符串掃描 |
vsscanf | (const char*, const char*, va_list) | 字符串可變參核心 |
轉發模式範式
// 通用封裝模板
int wrapper_scanf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = vscanf(fmt, ap); // 或 vfscanf/vsscanf
va_end(ap);
return ret;
}
為什麼需要 v* 系列函數?
- 二次封裝:自定義格式檢查、日誌記錄、參數預處理後再調用標準掃描。
- 避免可變參傳遞開銷:內部實現直接操作
va_list,無需再次展開。
- 線程安全上下文:某些實現中
va_list 可跨函數傳遞而不複製棧幀。
注意事項
vscanf 等函數屬於 C99 標準,現代編譯器(GCC、Clang、MSVC)均支持。
- 格式字符串
fmt 與後續參數必須嚴格匹配,否則未定義行為(如 %d 對應 float*)。
- 返回值:成功賦值的項目數,EOF 表示輸入失敗/結束。
對比:printf 族與 scanf 族的可變參對應關係
printf -> vprintf (stdout)
fprintf -> vfprintf (FILE*)
sprintf -> vsprintf (char* buffer)
snprintf -> vsnprintf (char* buffer, size_t)
scanf -> vscanf (stdin)
fscanf -> vfscanf (FILE*)
sscanf -> vsscanf (const char* buffer)
理解 vscanf 等核心函數的存在意義,能讓你在需要自定義輸入解析邏輯(如協議解析、配置文件讀取、測試框架斷言)時,靈活構建類 scanf 的 DSL 接口。
- 上一篇文章: C語言可變參實戰:實現參數累加求和函數 add(count, ...)
- 下一篇文章: C語言 do-while(0) 在 Linux 內核宏中的妙用:保證宏展開語句級安全
Copyright ©
程式碼之家 All Rights Reserved