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

C語言不定參數深度解析:從原理到實戰

     編輯:關於C

引言

在 C 語言開發中,不定參數(Variadic Functions)是實現像 printf、scanf 這類函數的關鍵機制。本文將通過實例、標準庫宏、手動實現以及底層宏定義四個維度,系統講解不定參數的工作原理與使用技巧。

一、不定參數基礎實例

最直觀的理解方式是觀察參數在棧上的分佈。下面的示例通過指針運算直接訪問棧上的參數:

#include <stdio.h>

/* 函數參數以棧形式存取,從右至左入棧 */
void fun(int a, ...) {
    int *temp = &a;
    temp++;  // 指向第一個可變參數
    int i;
    for (i = 0; i < a; ++i) {
        printf("%d\n", *temp);
        temp++;
    }
}

int main() {
    int a = 1, b = 2, c = 3, d = 4;
    fun(4, a, b, c, d);  // 輸出 1 2 3 4
    return 0;
}

注意:此方法強依賴編譯器棧布局,屬於非標準寫法,僅供原理演示。

二、標準庫 <stdarg.h> 規範用法

ANSI C 提供了 va_list、va_start、va_arg、va_end 四個宏,實現可移植的不定參數處理:

  • va_start(ap, last_fixed):初始化 ap 指向第一個可選參數
  • va_arg(ap, type):取得當前參數並推進到下一個
  • va_end(ap):清理 ap

示例:打印任意數量的字符串,以空字符串結束

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

int demo(char *msg, ...) {
    va_list argp;
    int argno = 0;
    char *para;

    va_start(argp, msg);
    while (1) {
        para = va_arg(argp, char*);
        if (strcmp(para, "") == 0) break;
        printf("Parameter #%d is: %s\n", argno, para);
        argno++;
    }
    va_end(argp);
    return 0;
}

int main(void) {
    demo("DEMO", "This", "is", "a", "demo!", "");
    return 0;
}

三、手動實現簡易 printf

不依賴 stdarg.h,直接操作棧內存模擬格式化輸出:

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

void myprintf(char *fmt, ...) {
    char *pArg = NULL;
    char c;
    pArg = (char *)&fmt;
    pArg += sizeof(fmt);  // 跳過 fmt 指針本身

    do {
        c = *fmt;
        if (c != '%') {
            putchar(c);
        } else {
            switch (*++fmt) {
                case 'd':
                    printf("%d", *((int *)pArg));
                    break;
            }
            pArg += sizeof(int);
        }
        ++fmt;
    } while (*fmt != '\0');
    pArg = NULL;
}

int main(void) {
    myprintf("the fist : %d\nthe secound : %d\n", 1, 2);
    return 0;
}

四、stdarg.h 宏定義底層詳解

以 VC++6.0 x86 平台為例,宏定義揭示了棧指針運算的本質:

#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)
  • _INTSIZEOF 保證參數按 int 大小對齊
  • va_start 利用最後一個固定參數地址計算第一個可變參數地址
  • va_arg 先推進指針再回溯取值
  • va_end 將指針置空

五、C++ 中的完整示例

#include <iostream>
#include <stdarg.h>

const int N = 5;
using namespace std;

void Stdarg(int a1, ...) {
    va_list argp;
    int i, ary[N];

    va_start(argp, a1);
    ary[0] = a1;
    for (i = 1; i < N; i++)
        ary[i] = va_arg(argp, int);
    va_end(argp);

    for (i = 0; i < N; i++)
        cout << ary[i] << endl;
}

void main() {
    Stdarg(5, 12, 64, 34, 23);
}

總結

不定參數的核心在於棧幀布局與指針運算。實際開發中應優先使用 <stdarg.h> 保證可移植性;手動操作棧僅適合教學或極受限環境。掌握其原理有助於理解調用約定、調試棧損壞等高階問題。

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