首页 > 代码库 > 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是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
第二个参数是在变参表前面紧挨着的一个变量;
同时把 ap 的位置指向变参表的下一个变量位置;
应该养成获取完参数表之后关闭指针的习惯。
示例一: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 (转载的上篇英文,原文打不开连接的参考下)