`printf` 是 C 語言最常用的輸出函式,其核心在於可變參數與格式字串解析。本文將從零手寫一個簡化版 `my_printf`,支援 `%d`(整數)、`%c`(字元)、`%s`(字串)與普通字元輸出,深入理解 `stdarg.h` 機制。
| 巨集 | 用途 |
|---|---|
va_list | 宣告一個變數,用於遍歷參數列表 |
va_start(ap, last_fixed_arg) | 初始化 `ap` 指向第一個可變參數 |
va_arg(ap, type) | 取得目前參數值,並將 `ap` 移至下一個參數 |
va_end(ap) | 清理 `ap`,結束參數存取 |
#include#include #include // 遞迴輸出整數(處理負數) void print_int(int n) { if (n < 0) { putchar('-'); n = -n; } if (n >= 10) print_int(n / 10); putchar('0' + n % 10); } // 簡易 my_printf void my_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); while (*fmt) { if (*fmt == '%') { fmt++; // 移至格式字元 switch (*fmt) { case 'd': { int val = va_arg(ap, int); print_int(val); break; } case 'c': { // char 會被提升為 int 傳遞 int val = va_arg(ap, int); putchar(val); break; } case 's': { char *s = va_arg(ap, char *); while (*s) putchar(*s++); break; } default: // 未知格式:輸出原始 %? putchar('%'); putchar(*fmt); } } else { putchar(*fmt); } fmt++; } va_end(ap); } int main() { my_printf("Hello %s! Number: %d, Char: %c\n", "World", 42, 'X'); // 輸出: Hello World! Number: 42, Char: X return 0; }
| 功能 | 實作重點 |
|---|---|
| 寬度/精度(%10d, %.2f) | 解析 `%` 後的數字與 `.`,配合 `printf` 族函式或手寫填充邏輯 |
| 浮點數 %f | 需實作浮點數轉字串(非易事),通常呼叫 `sprintf` 或 `dtoa` |
| 長度修飾符(l, ll, h) | `va_arg(ap, long)` / `long long` / `short`(提升為 int) |
| 指標 %p | `va_arg(ap, void*)` 配合十六進位輸出 |
| 返回輸出字元數 | 累計計數器,最後 `return count` |
實作 `my_printf` 是理解 C 語言可變參數機制、格式解析與型別提升的最佳練習。雖然生產環境直接使用標準庫,但掌握其內部運作能讓你在除錯格式化字串漏洞、撰寫嵌入式輸出函式、設計自訂日誌系統時得心應手。