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

C語言可變參數函數兩種實現:手動棧解析與 stdarg.h 標準庫

     編輯:關於C語言

前言

C 語言可變參數函數(如 `printf`)實現原理常被忽視。本文對比兩種做法:手動棧帧解析(教學用、非標準、不可移植)與 `` 標準宏(可移植、類型安全),並附帶 `MyPrintf` 完整實現。

方法一:手動棧解析(僅供原理演示)

#include <stdio.h>
#include <assert.h>

// 假設 cdecl 呼叫約定、參數右至左入棧、int 為 4 字節
long sum(int num, ...) {
    assert(num > 0);
    int *p = &num + 1;  // 緊鄰 num 之後的棧位置
    long ret = 0;
    while (num--) ret += *p++;
    return ret;
}

缺點:依賴特定 ABI(調用約定、棧增長方向、參數對齊、寄存器傳參),x64 / ARM / 非 cdecl 環境全部失效,**嚴禁生產代碼使用**。

方法二:標準 `` 宏(正規做法)

四大宏

宏用途
`va_list ap`定義參數列表游標
`va_start(ap, last_fixed)`初始化游標指向第一個可變參數
`va_arg(ap, type)`取出當前參數並推進游標(需知類型)
`va_end(ap)`清理(某些平台需釋放資源)
`va_copy(dest, src)` (C99)複製游標(多次遍歷必須)

實現 `MyPrintf`(類 `printf` 格式化輸出)

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

void MyPrintf(const TCHAR *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);

    // 1. 計算所需緩衝區大小(_vscprintf 不含結尾 \0)
    int len = _vsctprintf(fmt, ap) + 1;
    if (len <= 0) { va_end(ap); return; }

    // 2. 分配緩衝區
    TCHAR *buf = (TCHAR *)malloc(len * sizeof(TCHAR));
    if (!buf) { va_end(ap); return; }

    // 3. 重新遍歷參數列表格式化(需 va_copy 或 va_start 再次)
    va_end(ap);
    va_start(ap, fmt);
    _vstprintf_s(buf, len, fmt, ap);

    // 4. 輸出並釋放
    _putts(buf);
    free(buf);
    va_end(ap);
}

測試代碼

int _tmain() {
    long s = sum(3, 10, 110, 200);  // 320
    MyPrintf(_T("%d %c %d"), 123, _T('<'), 456);
    MyPrintf(_T("%s"), _T("This is a string"));
    getchar();
    return 0;
}

進階話題:類型安全與現代替代方案

  • 格式字串檢查:GCC/Clang `-Wformat` 編譯期檢查 `printf` 格式與參數匹配;自定義函數可加 `__attribute__((format(printf, 1, 2)))`。
  • C++ 變參模板:C++11 `template void log(Args&&... args)` 類型安全、零開銷、支援任意類型。
  • fmtlib / std::format (C++20):`fmt::format("{} {}", 1, 2.0)` 類型安全、高效、現代化首選。

總結對比

維度手動棧解析C++ 變參模板
可移植性極差標準保證標準 C++11
類型安全無程序員自負編譯器保證
效能極快(無函數調用)極快(宏展開)零開銷(內聯)
適用場景教學/嵌入式極限優化C 語言通用可變參數現代 C++ 新代碼
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved