va_start、va_arg、va_end、va_copy 可变参函数


1、应用与原理

        在C语言中,有时我们无法给出一个函数参数的列表,比如:
  int printf(const char *format, ...);
  int fprintf(FILE *stream, const char *format, ...);

    这时我们使用到了可以变参数,也就是使用...代表0个或多个参数。
    那么编译器如何获取/使用这些参数。这涉及到参数的传递原理:
参数传递原理:
    在内存中,函数的参数以栈的方式存取,从右到左入栈。这些参数存放的地址是连续的。这样,我们就可以通过获取第一个参数的地址,以及各个参数的地址偏移量,就可以获取每个参数的地址,从而得到每一个参数。

2、va_start、va_arg、va_end、va_copy介绍

       #include <stdarg.h>
       void va_start(va_list ap, last);
       type va_arg(va_list ap, type);
       void va_end(va_list ap);
       void va_copy(va_list dest, va_list src);

va_list是一个指向参数首地址的指针,
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;

va_start     
        对ap进行了一系列的初始化,之后ap会被va_arg和va_end使用到。last是可变参数...的前一个参数,如fun(char *fmt, ...)   的fmt。通过va_start初始化ap,我们就获得了可变参数前一个参数fmt的地址。
va_arg   
        va_arg用于获取可变参数...的每一个参数。如函数fun(char *fmt, ...)的一次调用fun(fmt, arg1, arg2, arg3)。在使用va_start()进行ap的初始化后,我们调用一次va_arg(ap,type)就获得了参数arg1,在调用一次就获得arg2,……从而,得到每一个参数的值。
va_end 
        va_end用于清理ap的值,与va_start()配对使用。  
va_copy 
        不常用,暂不介绍。

3、Example  

    先给一个man手册里的例子
//foo.c
#include <stdarg.h>
#include <stdio.h>
void
foo(char *fmt, ...)
{
   va_list ap;
   int d;
   char c, *s;
   va_start(ap, fmt);
   while (*fmt)
       switch (*fmt++) {
       case 's':              /* string */
           s = va_arg(ap, char *);
           printf("string %s
", s);
           break;
       case 'd':              /* int */
           d = va_arg(ap, int);
           printf("int %d
", d);
           break;
       case 'c':              /* char */
           /* need a cast here since va_arg only
           /* need a cast here since va_arg only
              takes fully promoted types */
           c = (char) va_arg(ap, int);
           printf("char %c
", c);
           break;
       }
   va_end(ap);
}

//main.c
#include <stdio.h>
#include <stdarg.h>
#include "foo.h"
int
main(void)
{
        foo("%s %d %c %d %c", "Hello", 4, 'x', 3, 'y');
        return 0;
}


执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe 
string Hello
int 4
char x
int 3
char y
windeal@ubuntu:~/Windeal/apue$


使用vsprintf的例子
//foo.c
#include <stdarg.h>
#include <stdio.h>
void
foo(char *fmt, ...)
{
        va_list ap;
        int len = 0;
        char buf[64];
        va_start(ap, fmt);
        len = vsnprintf(buf, 128, fmt, ap);
        va_end(ap);
        int i = 0;
        for(i = 0; i < len; i++)
        {
                putchar(buf[i]);
        }
        return ;
}

// main.c
#include <stdio.h>
#include <stdarg.h>
#include "foo.h"
int
main(void)
{
        foo("Test:%s %d %c %d %c
", "Hello", 4, 'x', 3, 'y');
        return 0;
}


执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe 
Test:Hello 4 x 3 y

































va_start、va_arg、va_end、va_copy 可变参函数va_start、va_arg、va_end、va_copy 可变参函数

原文地址:https://www.cnblogs.com/Windeal/p/4284636.html