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

C 語言手寫簡易 printf:可變參數、格式解析、itoa 實現全解析

     編輯:關於C語言

一、實現目標

  • 支援 %d(有符號十進制)、%s(字符串)格式。
  • 使用 <stdarg.h> 處理可變參數。
  • 自行實現 itoa(整數轉字符串),不依賴庫函數。

二、核心代碼


#include <stdio.h>
#include <stdarg.h>

/* 簡易 itoa:支援 2-36 進制,處理負數 */
char* my_itoa(int val, char* buf, int radix) {
    char* p = buf;
    char* first = buf;
    char temp;
    int digit;

    if (val < 0 && radix == 10) {
        *p++ = '-';
        val = -val;
        first = p;
    }

    do {
        digit = val % radix;
        val /= radix;
        *p++ = (digit > 9) ? (char)(digit - 10 + 'A') : (char)(digit + '0');
    } while (val > 0);

    *p-- = '\0';
    // 反轉字符串(first 到 p)
    while (first < p) {
        temp = *p; *p = *first; *first = temp;
        --p; ++first;
    }
    return buf;
}

/* 簡易 printf */
void my_printf(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);

    while (*fmt) {
        if (*fmt != '%') {
            putchar(*fmt++);
            continue;
        }
        switch (*++fmt) {
            case 's': {
                const char* s = va_arg(ap, const char*);
                while (*s) putchar(*s++);
                break;
            }
            case 'd': {
                int d = va_arg(ap, int);
                char buf[16];
                my_itoa(d, buf, 10);
                for (char* s = buf; *s; ++s) putchar(*s);
                break;
            }
            default:
                putchar(*fmt); // 未知格式原樣輸出
                break;
        }
        ++fmt;
    }
    va_end(ap);
}

int main() {
    int a = 154;
    char* p = "adasd";
    my_printf("%d\t%s\n", a, p);
    my_printf("%d\t%s\t%s\n", 12, "abcde", "a");
    return 0;
}

三、關鍵技術點解析

1. 可變參數機制 (<stdarg.h>)

  • va_list ap:參數列表指針。
  • va_start(ap, last_fixed):初始化,定位到第一個可變參數。
  • va_arg(ap, type):按類型取參數並移動指針。
  • va_end(ap):清理(某些平台需調用)。

2. 格式字符串解析

  • 單字符掃描,遇到 %c 則根據 c 分派。
  • 本例僅處理 d/s,易擴展 x/o/c/f 等。
  • 注意:標準 printf 還支援寬度、精度、標誌(%-10.5d),實現複雜度指數級上升。

3. itoa 實現細節

  • 負數處理:僅十進制時加 '-',並取絕對值(注意 INT_MIN 絕對值溢出,實際應用需用 unsigned 處理)。
  • 數字提取:反復 %、/ 得到低位到高位,寫入緩衝區。
  • 反轉:雙指針原地交換,得到正序字符串。
  • 進制支援:radix 參數化,2-36 進制通用。

四、擴展練習建議

  1. 增加 %x(十六進制)、%o(八進制)、%c(字符)、%p(指針)。
  2. 支援寬度 %10d、精度 %.5d、標誌 %-+0。
  3. 返回輸出字符數(標準 printf 行為),需在每次 putchar 計數。
  4. 實現 vsprintf / snprintf 將結果寫入緩衝區而非 stdout。
  5. 浮點數 %f 需自行實現 dtoa(Gruson/Ryu 算法),難度較大。

五、標準庫實現對比

特性本簡易版glibc/MSVC printf
格式支援%d %s完整 C99/C11
線程安全否是(鎖/線程局部存儲)
浮點數無Ryu/Gruson 高性能算法
本地化無支援 locale 十進制點等
返回值無輸出字符數

六、總結

手寫 printf 是理解可變參數、格式解析、數字轉字符串、字符輸出等底層機制的最佳練習。雖然生產代碼直接用標準庫,但掌握其原理有助於調試格式化漏洞、實現嵌入式無標準庫環境下的輸出、編寫自定義日誌框架等。

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