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

從 printf 看 C 語言可變參數函數實現原理:va_list/va_start/va_arg/va_end

     編輯:關於C語言

printf 可變參數之謎

printf(fmt, ...) 如何知道後續參數個數、類型、大小?答案在格式字符串 fmt 中。

標準宏機制


#include 

void my_printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);           // ap 指向 fmt 之後第一個參數
    char buf[1024];
    int n = vsprintf(buf, fmt, ap); // 交給 vsprintf 解析 fmt 並從 ap 取值
    va_end(ap);
    write(1, buf, n);
}

va_list 內部原理(x86 32 位為例)


typedef char *va_list;
#define _INTSIZEOF(n)   ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v))
#define va_arg(ap, t)   (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define va_end(ap)      (ap = (va_list)0)
  • 棧幀佈局:參數從右向左入棧,fmt 在最頂端。
  • va_start 計算 fmt 地址 + 大小(對齊後)= 下一個參數地址。
  • va_arg 根據類型 t 計算大小,移動指針並解引用取值。
  • 每個參數至少佔 sizeof(int) 位元組(小類型提升為 int)。

自定義可變參數函數範例


/* 返回多個 int 參數的最大值,第一個參數指定個數 */
int Max(int n, ...) {
    va_list ap;
    va_start(ap, n);
    int max = va_arg(ap, int);
    for (int i = 1; i < n; i++) {
        int v = va_arg(ap, int);
        if (v > max) max = v;
    }
    va_end(ap);
    return max;
}

/* 調用 */
int m = Max(3, 45, 12, 56);  // 返回 56

格式字符串驅動的類型安全

  • printf("%d %f", 1, 2.0):%d 讀 int,%f 讀 double(float 提升)。
  • 若實參類型與格式不符 → 未定義行為(崩潰/錯誤輸出)。
  • 編譯器 -Wformat 可檢查格式字符串與參數匹配度。

避坑指南

  • 實際參數個數 不能少於 格式字符串要求,否則讀越界導致崩潰。
  • 參數類型大小 不能小於 格式要求(如傳 int 給 %f),否則讀取鄰近棧內容。
  • 大於要求無害(多餘參數被忽略)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved