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

C語言可變參數實現原理:va_list、va_start、va_arg、va_end 深度解析

     編輯:關於C語言

可變參數函數原型

int printf(const char *format, ...);

透過 ... 表示參數個數可變,配合 format 字串解析實際引數。

標準庫實作機制 (stdarg.h)

核心包含一個資料結構與三個巨集:

  • va_list:引數列表指標
  • va_start(ap, last_fixed):初始化 ap 指向第一個可變參數
  • va_arg(ap, type):取得目前參數並推進 ap
  • va_end(ap):清理(通常為空操作)

自製簡易 printf 範例

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

void myprintf(char *format, ...) {
    va_list ap;
    va_start(ap, format);
    
    char buf[64];
    int pos = 0;
    
    while (format[pos]) {
        if (format[pos] == '%') {
            pos++;
            switch (format[pos]) {
                case 'd': case 'u': {
                    int val = va_arg(ap, int);
                    sprintf(buf, "%d", val);
                    write(STDOUT_FILENO, buf, strlen(buf));
                    break;
                }
                case 'f': {
                    double val = va_arg(ap, double);
                    sprintf(buf, "%f", val);
                    write(STDOUT_FILENO, buf, strlen(buf));
                    break;
                }
            }
            pos++;
        } else {
            write(STDOUT_FILENO, &format[pos], 1);
            pos++;
        }
    }
    va_end(ap);
}

int main() {
    myprintf("i = %d, u = %u, f = %f\n", -1, 5, 0.2);
    return 0;
}

輸出:i = -1, u = 5, f = 0.200000

Linux 核心原始碼實作分析

typedef char *va_list;

#define _AUPBND (sizeof(acpi_native_int) - 1)  // 對齊邊界
#define _ADNBND (sizeof(acpi_native_int) - 1)

#define _bnd(X, bnd) (((sizeof(X)) + (bnd)) & (~(bnd)))

#define va_start(ap, A) (void)((ap) = (((char *)&(A)) + _bnd(A, _AUPBND)))
#define va_arg(ap, T) (*(T *)(((ap) += _bnd(T, _AUPBND)) - _bnd(T, _ADNBND)))
#define va_end(ap) (void)0

關鍵點解析

  1. va_list 本質為 char*,指向堆疊上參數區域
  2. va_start 計算最後一個固定參數地址,加上對齊位移,指向第一個可變參數
  3. va_arg 先推進指標(含對齊),再回溯取值,確保型別正確存取
  4. 對齊機制:_bnd 確保參數按機器字長對齊存取

記憶體模型視角

C 函數呼叫慣例(如 __cdecl)將參數由右向左壓入堆疊,參數在記憶體中連續分佈。固定參數位置已知,可透過指標運算遍歷後續可變參數。

注意事項

  • 至少需一個固定參數作為定位基準
  • float 傳遞時會提升為 double,va_arg 需用 double
  • 呼叫端與被呼叫端需約定參數解析協定(如 printf 的格式字串)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved