STDARG(3)翻译

名字

stdarg, va_start, va_arg, va_end, va_copy可变参数列表。

概要

#include 

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类型,并且定义3个宏来处理被调函数的参数列表。被调函数必须声明一个被va_start, va_arg和va_end使用的类型为va_list的对象。

va_start()

va_start()宏初始化一个被后面va_arg()和va_end()使用的ap,并且必须率先调用。last是在可变参数列表之前的最后一个我们知道类型的参数的名字。因为va_start()宏可能会用到last的地址,所以不要把last声明成寄存器变量或者函数、数组类型。

va_arg()

va_arg()这个宏扩展到调用中的下一个参数的值和类型。ap就是由va_start()初始化的va_list类型的ap。每次调用va_arg(),它都会修改ap,使它的下一次调用返回下一个参数。参数type就是一个类型名,这样就可以获取一个类型对象了。

在调用va_start()之后第一次调用va_arg()它就返回last后面的第一个参数。后面的调用,依次返回剩下的参数的值。

如果后面没有其它参数了,或者type和实际的下一个参数不匹配(会按照默认参数扩展?),就会产生随机的错误。

如果ap在调用va_arg(ap, type)的时候是指向一个函数的,那么在这个函数返回的时候ap的值是未定义的。

va_end()

每次在一个函数里面调用va_start(),必须有一个va_end()与它进行匹配。用多对va_start()和va_end()多次遍历一个va_list也是可以的。va_end()可以是一个宏或者一个函数。

va_copy()

va_copy()将之前初始化的可变参数列表从src拷贝到dest。这个行为等价于先对dest做va_start(),然后调用获得src相同次数的va_arg()。

一个显而易见的实现是让va_list指向可变函数的栈帧。这样的话(目前最常见),这样的话,赋值操作好像也没什么问题。

va_list aq = ap;

不幸的是,有些系统也把va_list实现成一个指针数组。那这样的话就需要:

va_list aq;
*aq = *ap;

最后,对于参数传递到寄存器的系统,就有必要用va_start()分配内存,存储参数,并且表明哪个参数是下一个。这样的话va_arg()才可以对参数进行遍历。然后va_end()可以释放内存。为了解决这个问题,C99添加了一个宏va_copy()。那么上面的赋值就可以用下面的函数代替:

va_list aq;
va_copy(aq, ap);
...
va_end(aq);

每次调用va_copy(),必须在同一个函数里面调用对应的va_end()。有些系统不提供va_copy(),但是有__va_copy,这个是草案里面使用的名字。

属性

这部分使用的术语的解释可以参看attribute(7)。

接口 属性
va_start(), va_end(), va_copy() 线程安全性 MT-safe
va_arg() 线程安全性 MT-safe race:ap

规范

va_start(), va_arg(), va_end()宏遵守C89,va_copy()遵守C99。

注释

这些宏和它们代替的历史上的宏并不兼容。一个后向兼容的版本可以在头文件中找到。

历史的背景是:

#include 

void
foo(va_alist)
    va_dcl
{
    va_list ap;

    va_start(ap);
    while (...) {
        ...
        x = va_arg(ap, type);
        ...
    }
    va_end(ap);
}

有些系统,va_end包含一个“}”,va_start包含一个“{”,这样的话两个宏就必须出现在同一个函数里面,否则编译就不会通过。

BUGS

不像varargs宏,stdarg宏不允许程序员编码一个没有固定参数的函数。这个问题主要是在将varargs代码转换为stdarg代码的时候引入了很多工作量。当然,这个也给从可变参数函数内部将它所有的参数传递给一个有一个va_list参数的函数(比如vfprintf(3))带来了很多的困难。

例子

函数foo把一个格式字符串作为参数,并且根据每个格式字符表明的类型打印出每个参数。

#include 
#include 

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\n", s);
            break;
        case 'd':              /* int */
            d = va_arg(ap, int);
            printf("int %d\n", d);
            break;
        case 'c':              /* char */
            /* Need a cast hear since va_arg only
               takes fully promoted types */
            c = (char) va_arg(ap, int);
            printf("char %c\n", c);
            break;
        }
    va_end(ap);
}

版权

本页是Linux man-page项目4.15版的一部分。关于项目的描述,报bug相关信息,以及本页的最新版本可以在https://www.kernel.org/doc/man-pages/找到。

你可能感兴趣的:(C语言)