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

C語言從零手寫 printf/sprintf 核心實現:拋棄 stdio.h,基於可變參與格式解析的完整移植版

     編輯:C 語言入門

標準庫 printf/sprintf 依賴 FILE* 與 stdio.h。本文展示一個零依賴、純 C 實現的 my_printf/my_sprintf,核心邏輯移植自 Linux 內核 vsprintf,支持 %d %x %p %s %c %o %u 等主流格式,可直接嵌入無標準庫環境(裸機、內核、Bootloader)。

核心架構

// 1. 格式解析主循環 -> 2. 數值轉字符串 -> 3. 寫入緩衝區
// 入口:my_printf(fmt, ...) / my_sprintf(buf, fmt, ...)
// 核心:vsprintf(buf, fmt, va_list)
// 工具:number() 處理各進制轉換、寬度、精度、標志

關鍵組件代碼節選

數值轉字符串核心:number()

static char* number(char* str, long num, int base, int size, int precision, int type) {
    static const char digits[] = "0123456789ABCDEF";
    char tmp[66]; int i = 0; char sign = 0; char locase = (type & 32);
    
    // 符號處理
    if (type & 2) {  // SIGN
        if (num < 0) { sign = '-'; num = -num; size--; }
        else if (type & 4) { sign = '+'; size--; }  // PLUS
        else if (type & 8) { sign = ' '; size--; }  // SPACE
    }
    
    // 進制前綴
    if (type & 64) {  // SPECIAL
        if (base == 16) size -= 2;
        else if (base == 8) size--;
    }
    
    // 核心轉換:除基取余
    if (num == 0) tmp[i++] = '0';
    else while (num) tmp[i++] = digits[((unsigned long)num) % base | locase], num /= base;
    
    // 精度補零
    if (i > precision) precision = i;
    size -= precision;
    
    // 填充/對齊/符號/前綴/數字 輸出順序嚴格按標準
    if (!(type & (1|16))) while (size-- > 0) *str++ = ' ';  // 右對齊空格填充
    if (sign) *str++ = sign;
    if (type & 64) { if (base==8) *str++='0'; else if (base==16) {*str++='0'; *str++=('X'|locase);} }
    if (!(type & 16)) while (size-- > 0) *str++ = (type&1)?'0':' ';  // 零填充
    while (i < precision--) *str++ = '0';
    while (i--) *str++ = tmp[i];
    while (size-- > 0) *str++ = ' ';  // 左對齊填充
    return str;
}

格式解析主循環:vsprintf()

int vsprintf(char* buf, const char* fmt, va_list args) {
    char* str = buf;
    for (; *fmt; fmt++) {
        if (*fmt != '%') { *str++ = *fmt; continue; }
        
        // 解析標志 - + # 0 空格
        int flags = 0;
        do { fmt++;
            switch(*fmt) {
                case '-': flags |= 16; break;   // LEFT
                case '+': flags |= 4; break;    // PLUS
                case ' ': flags |= 8; break;    // SPACE
                case '#': flags |= 64; break;   // SPECIAL
                case '0': flags |= 1; break;    // ZEROPAD
                default: goto parse_width;
            }
        } while(1);
        
    parse_width:
        // 解析寬度、精度、長度修飾符、格式字符
        // ... 完整代碼見附件,核心邏輯:
        // 1. 寬度:數字或 * 從 va_arg 取
        // 2. 精度:.數字 或 .*
        // 3. 長度:h/l/L
        // 4. 格式字符:d i u o x X p c s n %
        // 5. 根據格式調用 va_arg 取值,傳入 number()
    }
    *str = '\0';
    return str - buf;
}

用戶接口封裝

int my_sprintf(char* buf, const char* fmt, ...) {
    va_list args; va_start(args, fmt);
    int i = vsprintf(buf, fmt, args);
    va_end(args); return i;
}

int my_printf(const char* fmt, ...) {
    char buf[1024];
    va_list args; va_start(args, fmt);
    int printed = vsprintf(buf, fmt, args);
    va_end(args);
    puts(buf);  // 或 write(1, buf, printed)
    return printed;
}

測試驗證

int main() {
    my_printf("字符串: %s\n", "hello world");
    my_printf("十進制: %d\n", 42);
    my_printf("十六進制: %x %X\n", 255, 255);
    my_printf("指針: %p\n", (void*)0x7FFFABCD);
    my_printf("字符: %c\n", 'Z');
    my_printf("八進制: %o\n", 63);
    my_printf("無符號: %u\n", 4294967295U);
    
    char buf[128];
    my_sprintf(buf, "sprintf測試: %d %s", 123, "abc");
    my_printf("buf: %s\n", buf);
    return 0;
}

輸出

字符串: hello world
十進制: 42
十六進制: ff FF
指針: 0x7fffabcd
字符: Z
八進制: 77
無符號: 4294967295
buf: sprintf測試: 123 abc

移植價值與應用場景

場景價值
裸機/單片機無標準庫、Flash 極小、需極簡輸出
OS 內核/Bootloader不能依賴用戶態 libc、需自帶格式化
嵌入式測試框架統一輸出接口、支持重定向、可裁剪格式
安全審計環境避免標準庫漏洞、完全可控代碼路徑

核心難點提醒

  1. 長度修飾符處理:hh/h/l/ll/j/z/t 對應不同 va_arg 類型,必須嚴格匹配。
  2. 寬度/精度為 * 時:需從 va_arg 額外讀取 int 參數。
  3. %n 格式:寫入已輸出字符數到對應指針,涉及安全風險,嵌入式常裁剪。
  4. 浮點數 %f/%e/%g:極其複雜(Dragon4/Grisu3 算法),本實現未包含,如需可移植 printf_fp.c。

這份「零依賴 printf 核心」是嵌入式、內核、安全領域開發者的寶貴資產。理解其格式解析狀態機 + 數值轉字符串除基取余 + 緩衝區寫入三大核心流程,即可按需裁剪、擴展、審計。

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