曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 6512|回复: 0
打印 上一主题 下一主题

va_start(), va_arg(), va_end()

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2009-7-12 19:04:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
名称
stdarg, 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);

描述
也许有时会需要不同类型、不同数目的参数来调用函数。<stdarg.h> 声明了一个 va_list 类型,还定义了三个宏来逐步跟踪一个参数列表。对于调用函数来说,它并不会知道这个列表里的参数个数及其类型。

调用函数必须声明一个 va_list 类型的变量。va_list 类型在宏 va_start(),va_arg(),和 va_end() 中用到。

va_start()
va_start() 初始化 ap 这个参数,这是为了后面的 va_arg() 和 va_end() 会用到此参数。这个宏是必须被第一个调用的。
last  参数是可变参数列表之前的最近的一个参数的名字,也就是说,这是调用函数 所知道的最近的一个类型。
对于 last 的翻译似乎很是晦涩。看下面的具体应用:
void err_sys(const char *fmt, ...)
{
    va_list        ap;

    va_start(ap, fmt);
   .....
}
上面的程序片段中,err_sys() 是我们自定义的一个函数。在 err_sys() 中有参数 fmt,也就是 va_start() 中的 last 。这个参数位于可变参数列表(用 ... 表示)之前。也就是说,err_sys() 函数一开始明确就能知道参数。
由于这个参数( last )的地址可能会在 va_start() 中用到,所以它不能是一个寄存器变量,或者是一个函数,抑或是一个数组类型。

va_arg()
va_arg() 宏展开成一个表达式,这个表达式含有调用函数中下一个参数的值和类型。
其中,ap 是经由 va_start() 初始化后的 va_list ap 。每次调用 va_arg() 都会改变 ap,这样一来下一次的调用就会返回下一个参数。参数 type 是一个已指定过的类型名(如 int , char ),这样一来,通过增加 * 到 type 中( 如 int *, char *)就可以简单地获得一个已指定类型对象的指针类型。
在调用 va_start() 后第一次使用 va_arg() 会返回 last 后的参数。连续调用则返回剩下的参数。
如果已没有下一个参数,或者 type 与下一个参数的实际类型并不兼容,则发生的错误不可预料。
如果把 ap 传递给一个调用 va_arg(ap, type) 的函数,那么当这个函数返回后,ap 的值为未定义。

va_end()
在同一个函数里,每一次调用 va_start() 都必须和 va_end() 相配对。在调用 va_end( ap ) 后,ap 变为未定义。对于多重遍历参数列表,则每次配对使用 va_start() 和 va_end() 是可以的。va_end() 可能是一个宏,或者是一个函数。

举例
#include <stdio.h>
#include <stdarg.h>

void foo(char *fmt, ...)
{
        va_list ap;
        int     d;
        char c, *s;

        va_start(ap, fmt);
        while (*fmt)
                switch (*fmt++) {
                case 's':
                        s = va_arg(ap, char *);
                        printf("string %s\n", s);
                        break;
                case 'd':
                        d = va_arg(ap, int);
                        printf("int %d\n", d);
                        break;
                case 'c':
                        c = (char) va_arg(ap, int);
                        printf("char %c\n", c);
                        break;
                }
        va_end(ap);
}
int main()
{
        int val1 = 20;
        char buf[20] = {"hello world"};
        foo("kkkkkk%dxxxxxsxxxxx",val1,buf);

        return 0;
}
      
运行及输出
beyes@linux-beyes:~/C> ./varg.exe
int 20
string hello world
说明
在 foo() 函数中,使用一个 while 循环遍历 fmt 所指向字符串中的每一个字符,这是为了要找出与 switch 所定制相匹配的字符。假如找到匹配项,那么就调用 va_arg()。在 va_arg() 中,ap 表示了 fmt 指向字符串后面的参数列表。每一次调用 va_arg() 就会读出参数列表中的一个项,假如得到的这个参数列表中的项的实际类型和 va_arg() 中的第二个参数指明的类型一样,那么这个宏调用成功。如果是乱指定一个未知的类型,比如 kk ,那么这在编译时也会给出错误的提示:
varg.c: In function ‘foo’:
varg.c:14: error: expected specifier-qualifier-list before ‘kk’
如果指定一个不兼容的类型,比如把上面的 char * 改写成 int,编译时给出提示:
varg.c: In function ‘foo’:
varg.c:14: warning: assignment makes pointer from integer without a cast
这是个警告信息,意思是没有通过类型强制转换就把一个整数类型变成指针类型。但程序运行后仍然打印出想要的信息。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-4 01:32 , Processed in 0.185158 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表