天天看點

可變參數使用

在C中,可變參數用于參數個數,類型不确定的情況,如printf,snprintf函數的實作。

當我們無法列出傳遞函數的所有實參的類型和數目時,可以用省略号指定參數表

void func(...);
void func(parm_list,...);           

這是C傳參的一種形式,與固定參數不同。

函數參數的傳遞原理

函數參數以棧的形式存儲,從右往左入棧。

舉個例子:

void func(int x, float y, char z);           

在調用函數的時候,實參z先入棧,然後是y,最後是x。在記憶體中變量的存放次序是x->y->z。是以,從理論上來說,隻要知道x,y,z其中一個變量的位址和類型,通過指針運算,可找到其他變量。

typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type ); 
void va_end ( va_list ap );            

va_list是指向目前參數的指針,通過這個指針進行取參。

宏的使用方式如下:

  1. 先定義一個va_list的變量,假設為ap
  2. 使用va_start對ap進行初始化,va_start的第一個參數是ap,第二個參數是變參表中“…”前面的那個參數
  3. 然後調用va_arg擷取參數,第一個參數還是ap,第二個參數是要擷取的參數的類型,并且ap指向下一個變量
  4. 擷取完參數後,使用va_end關掉ap。va_start和va_end通常成對出現。

例子:

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

int my_snprintf(char *dst, int size, char *fmt, ...)
{
    int len;
    va_list argp;
    va_start(argp, fmt);
    len = vsnprintf(dst, size, fmt, argp);
    len = len > size - 1 ? size - 1 : len;
    va_end(argp);
    
    return len;
}

int main(void)
{
    char str[8];
    int len;
    
    len = my_snprintf(str, sizeof(str), "A:%d:%s", 1, "ABCDEFGH");
    printf("str:%s, len:%d\n", str, len);

    return 0;
}