首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言模擬實作 printf:可變參數與格式化輸出核心原理

     編輯:關於C

前言

`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`,結束參數存取

格式字串解析策略

  1. 逐字元掃描格式字串。
  2. 遇到 `%`,檢查下一字元決定轉換規格。
  3. 根據規格從 `va_arg` 取出對應型別參數並輸出。
  4. 非 `%` 字元直接輸出。

完整實作程式碼

#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`

標準庫 vs 手寫版差異

  • 標準 `printf` 支援完整 C99/C11 規格、地區設定、執行緒安全、緩衝區控制。
  • 手寫版適合嵌入式系統(無標準庫)、教學理解、特殊格式需求。

常見坑點提醒

  1. 預設參數提升:`float` → `double`,`char`/`short` → `int`。`va_arg` 必須用提升後型別(如 `%c` 用 `int`)。
  2. 格式字串與實際參數不符:導致未定義行為(崩潰/輸出亂碼)。
  3. va_list 重複使用:需 `va_copy` 或重新 `va_start`。
  4. 缺少 va_end:部分 ABI 會導致堆疊損壞。

總結

實作 `my_printf` 是理解 C 語言可變參數機制、格式解析與型別提升的最佳練習。雖然生產環境直接使用標準庫,但掌握其內部運作能讓你在除錯格式化字串漏洞、撰寫嵌入式輸出函式、設計自訂日誌系統時得心應手。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved