typedef char *va_list;
在进程中,堆栈地址是从高到低分配的,当执行一个函数的时候,依次将函数参数列表,函数返回地址,函数的执行代码按照从高到低的地址入栈。
而函数参数列表按照从右到左的顺序入栈,即最右边的参数在堆栈地址的最高处,最左边的参数在堆栈最低地址处。
入栈过程中,堆栈指针不断递减。
比如函数arg_test(format, args1, args2, args3, args4)在堆栈中的地址如下:
对以上3个宏的解释
va_start(arg_ptr, format)得到第一个可变参数的地址,即得到args1参数的地址
根据定义:#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
(va_list)&v得到起始参数的地址,再加上_INTSIZEOF(v),其实就是起始参数下一个参数的地址,即第一个可变参数的地址。
va_arg(arg_ptr, type)根据当前指向的可变参数的类型type得到当前指向的可变参数的值,并且arg_ptr指针上移一个_INTSIZE(int),即指向下一个可变参数的地址。
va_end(arg_ptr) 清空arg_ptr指针,即arg_ptr=0
以上,读取可变参数的过程其实就是遍历堆栈中参数列表的过程,从低地址到高地址一个一个把参数内容读出来的过程。
实例1
解决读取参数个数的问题:在起始参数中指定后面可变参数的个数,这样可以在循环中读取所有的可变参数。
#include <stdio.h> #include <stdarg.h> void arg_cnt(int cnt, ...) { int i = 0; va_list arg_ptr; va_start(arg_ptr, cnt); for (i = 0; i < cnt; i++) { printf("value=%d ", va_arg(arg_ptr, int)); } printf("\n"); va_end(arg_ptr); } int main() { arg_cnt(5, 5, 4, 3, 2, 1); return 0; }
如果参数类型不定,那样就无法直接知道可变参数的个数的解决办法。
但有如下这种解决办法,将fmt当作一个字符串指针去遍历。
#include <stdio.h> #include <stdarg.h> #include <stdlib.h> void myprintf(char *fmt, ...) { char c; va_list arg_ptr; va_start(arg_ptr, fmt); do { c = *fmt; if (c != '%') printf("%c", c); else { switch(*++fmt) { case 'd': printf("%d", va_arg(arg_ptr, int)); break; case 'x': printf("%x", va_arg(arg_ptr, int)); break; default: break; } } ++fmt; } while(*fmt != '\0'); va_end(arg_ptr); } int main() { int i = 1234; int j = 5678; myprintf("the first test: i=%d\n", i); myprintf("the second test: i=%d j=%d %x\n", i, j, 0xabcd); return 0; }
#include <stdarg.h>
void my_printf(chat *fmt, ...)
{
va_list arg_ptr;
va_start(arg_ptr, fmt);
vprintk(fmt, arg_ptr); //fmt是格式字符串,arg_ptr指向第一个可变参数
va_end(arg_ptr);
}
以上内容有部分摘自:http://blog.csdn.net/joanlynnlove/article/details/7264835