首页 > 代码库 > va_list & va_start & va_arg & va_end

va_list & va_start & va_arg & va_end

           va_list 属于变量 而 va_start & va_arg & va_end  C语言中解决变参问题的一组宏。头文件来自stdarg.h。

查看linux系统源码方式我一般用locate stdarg.h,然后找到提示目录vi进去。源文件定义是:

在Mac 下追踪头文件也会发现如下宏定义:

typedef __darwin_va_list va_list;
typedef __builtin_va_list    __darwin_va_list;    /* va_list */

本质上都是__builtin_va_list   


    va_list是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。


   <Step 1> 在调用参数表之前,应该定义一个 va_list类型的变量,以供后用(假设这个 va_list 类型变量被定义为ap);
   <Step 2> 然后对 ap进行初始化,让它指向可变参数表里面的第一个参数。这是通过 va_start 来实现的,其第一个参数是 ap本身,

                  第二个参数是在变参表前面紧挨着的一个变量;
   <Step 3> 然后是获取参数,调用 va_arg。它的第一个参数是ap,第二个参数是要获取的参数的指定类型,并返回这个指定类型的值,

                   同时把 ap 的位置指向变参表的下一个变量位置;
   <Step 4> 获取所有的参数之后,我们有必要将这个 ap指针关掉,以免发生危险,方法是调用 va_end。它是将输入的参数 ap 置为NULL,

                  应该养成获取完参数表之后关闭指针的习惯。

示例一:

void simple_va_fun(int i, ...)
{
    va_list arg_ptr;
    
    va_start(arg_ptr, i);
    int num1 = va_arg(arg_ptr, int);
    int num2 = va_arg(arg_ptr, int);

    va_end(arg_ptr);
    printf("%d %d %d /n", i, num1, num2);
    return;
}
示例二:

    // Quick Information Display  
    void show(id formatstring,...)  
    {  
        va_list arglist;  
        if (!formatstring) return;  
        va_start(arglist, formatstring);  
        id outstring = [[[NSString alloc] initWithFormat:formatstring arguments:arglist] autorelease];  
        va_end(arglist);  
          
        UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"LEAK DEMO" message:outstring delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];  
        [av show];  
    }  
      
    - (void) intro  
    {  
        show(@"Run with Instruments using Leaks. Click a button to leak memory.%@\n",@"zyjzyj");  
    }  

示例三:

    #define showAlert(format, ...) myShowAlert(__LINE__, (char *)__FUNCTION__, format, ##__VA_ARGS__)  
      
    // Simple Alert Utility  
    void myShowAlert(int line, char *functname, id formatstring,...)  
    {  
          
        va_list arglist;  
        if (!formatstring) return;  
        va_start(arglist, formatstring);  
        id outstring = [[[NSString alloc] initWithFormat:formatstring arguments:arglist] autorelease];  
        va_end(arglist);  
          
        NSString *filename = [[NSString stringWithCString:__FILE__ encoding:NSUTF8StringEncoding] lastPathComponent];  
        NSString *debugInfo = [NSString stringWithFormat:@"%@:%d\n%s", filename, line, functname];  
          
        UIAlertView *av = [[[UIAlertView alloc] initWithTitle:outstring message:debugInfo delegate:nil cancelButtonTitle:@"OK"otherButtonTitles:nil] autorelease];  
        [av show];  
    }  
      
    - (void) rightAction: (id) sender  
    {  
        showAlert(@"You pressed the right button");  
    }  


      在C语言中,它们的详细定义如下:

1) va_list型变量:

#ifdef  _M_ALPHA
typedef struct {
        char *a0;       /* pointer to first homed integer argument */
        int offset;     /* byte offset of next parameter */
} va_list;
#else
typedef char *  va_list;
#endif

2)_INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

3)VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

4)VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

5)VA_END宏,清空va_list可变参数列表:

#define va_end(ap)      ( ap = (va_list)0 )

进一步解释:

va_list arg_ptr:定义一个指向个数可变的参数列表指针;

va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,

说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一个 固定参数;…之前的一个参数),

函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。如果有一va函数的声明是

void va_test(char a, char b, char c, …),则它的固定参数依次是a,b,c,最后一个固定参数argN为c,因此就是va_start(arg_ptr, c)。

va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中下一个参数。

va_copy(dest, src):dest,src的类型都是va_list,va_copy()用于复制参数列表指针,将dest初始化为src。

va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_ptr被置无效后,可以通过调用

va_start ()、va_copy()恢复arg_ptr。每次调用va_start() / va_copy()后,必须得有相应的va_end()与之匹配。

参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内。

va函数的实现就是对参数指针的使用和控制。这里,移动指针使其指向下一个参数,那么移动指针时的偏移量是多少呢,

没有具体答案,因为这里涉及到内存对齐(alignment)问题,内存对齐跟具体 使用的硬件平台有密切关系,

比如大家熟知的32位x86平台规定所有的变量地址必须是4的倍数(sizeof(int) = 4)。

va机制中用宏_INTSIZEOF(n)来解决这个问题,没有这些宏,va的可移植性无从谈起。


参考内容来源:

初级篇:

http://blog.sina.com.cn/s/blog_590be5290100qhxr.html

http://justsee.iteye.com/blog/1637173

http://www.cnblogs.com/rainduck/archive/2010/11/10/1873417.html

http://www.cnblogs.com/margincc/archive/2011/03/29/2095057.html#undefined

进阶篇:

http://www.cnblogs.com/diyunpeng/archive/2010/01/09/1643201.html

http://blog.csdn.net/ritaday/article/details/6718353(强烈推荐)

http://www.cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html (英文还可以的话,强烈推荐)

http://blog.csdn.net/skymingst/article/details/36872003 (转载的上篇英文,原文打不开连接的参考下)