va_list
2016-06-13 16:24
211 查看
va_list 是C语言中解决变参问题的一组宏(va,variable
argument,可变参数),这些宏定义在 stdarg.h 文件中。主要包括:
void
va_start( va_listarg_ptr, prev_param );
type va_arg( va_listarg_ptr, type );
void va_end( va_listarg_ptr );
(一) 示例
下面我们写一个简单的可变参数的函数,此函数至少有一个整数参数,其他参数也是整型的,不过是可选的。
void va_func(int i, ...)
{
va_list vl;
int j = 0;
va_start(vl, i);
j = va_arg(vl, int);
va_end(vl);
printf("%d %f\n", i, j);
return;
}
从这个函数的实现可以看到,使用可变参数应该有以下步骤:
1)在函数里定义一个va_list型的变量,这里是vl,这个变量是指向参数的指针.
2)用va_start宏初始化变量vl.这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
3)用va_arg返回可变的参数. va_arg的第二个参数是你要返回的参数的类型,这里是float型.
4)如果函数有多个可变参数的,依次调用va_arg获取各个参数.
5)最后用va_end宏结束可变参数的获取.此时,你就可以在函数里使用第N个参数了.
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)va_func(100);
结果是:100 -123456789(会变的值)
2)va_func(100,200);
结果是:100 200
3)va_func(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.
(二)可变参数在编译器中的处理
va_list、va_start、va_arg、va_end是在stdarg.h中被定义成宏的,由于硬件平台及编译器的不同,所以宏的定义也有所不同。
示例:
typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1)
)
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))
)
#define va_end(ap) ( ap = (va_list)0 )
示例解释:
_INTSIZEOF:主要用于某些需要内存对齐的系统。
va_list:被定义成char *,有一些平台或操作系统定义为void *.
va_start:&v是固定参数在堆栈的地址,_INTSIZEOF(v)是固定参数的内存大小,所以我们运行va_start(ap,
v)以后,ap指向第一个可变参数在堆栈的中地址。
va_arg:(ap += _INTSIZEOF(t))将ap指向下一个参数;((ap
+= _INTSIZEOF(t)) - _INTSIZEOF(t))获取本参数的地址;(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))将本参数的地址转换为t类型;(
*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )获取本参数的值。
va_end:清零。
(三)可变参数在编程中要注意的问题
va_list并不能智能地识别不同参数的个数和类型.
有人会问:printf中不是实现了智能识别参数吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
(四) va系列宏的兼容性.
System V Unix 把 va_start 定义为只有一个参数的宏:
va_start(va_list arg_ptr);
而 ANSI C 则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用 System V 的定义,应该用 vararg.h 头文件中所定义的宏。ANSI
C 的宏跟 System V 的宏是不兼容的。
参考文章:
http://jazka.blog.51cto.com/809003/232331/ http://blog.csdn.net/aihao1984/article/details/5953668
argument,可变参数),这些宏定义在 stdarg.h 文件中。主要包括:
void
va_start( va_listarg_ptr, prev_param );
type va_arg( va_listarg_ptr, type );
void va_end( va_listarg_ptr );
(一) 示例
下面我们写一个简单的可变参数的函数,此函数至少有一个整数参数,其他参数也是整型的,不过是可选的。
void va_func(int i, ...)
{
va_list vl;
int j = 0;
va_start(vl, i);
j = va_arg(vl, int);
va_end(vl);
printf("%d %f\n", i, j);
return;
}
从这个函数的实现可以看到,使用可变参数应该有以下步骤:
1)在函数里定义一个va_list型的变量,这里是vl,这个变量是指向参数的指针.
2)用va_start宏初始化变量vl.这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
3)用va_arg返回可变的参数. va_arg的第二个参数是你要返回的参数的类型,这里是float型.
4)如果函数有多个可变参数的,依次调用va_arg获取各个参数.
5)最后用va_end宏结束可变参数的获取.此时,你就可以在函数里使用第N个参数了.
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)va_func(100);
结果是:100 -123456789(会变的值)
2)va_func(100,200);
结果是:100 200
3)va_func(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.
(二)可变参数在编译器中的处理
va_list、va_start、va_arg、va_end是在stdarg.h中被定义成宏的,由于硬件平台及编译器的不同,所以宏的定义也有所不同。
示例:
typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1)
)
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))
)
#define va_end(ap) ( ap = (va_list)0 )
示例解释:
_INTSIZEOF:主要用于某些需要内存对齐的系统。
va_list:被定义成char *,有一些平台或操作系统定义为void *.
va_start:&v是固定参数在堆栈的地址,_INTSIZEOF(v)是固定参数的内存大小,所以我们运行va_start(ap,
v)以后,ap指向第一个可变参数在堆栈的中地址。
va_arg:(ap += _INTSIZEOF(t))将ap指向下一个参数;((ap
+= _INTSIZEOF(t)) - _INTSIZEOF(t))获取本参数的地址;(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))将本参数的地址转换为t类型;(
*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )获取本参数的值。
va_end:清零。
(三)可变参数在编程中要注意的问题
va_list并不能智能地识别不同参数的个数和类型.
有人会问:printf中不是实现了智能识别参数吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
(四) va系列宏的兼容性.
System V Unix 把 va_start 定义为只有一个参数的宏:
va_start(va_list arg_ptr);
而 ANSI C 则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用 System V 的定义,应该用 vararg.h 头文件中所定义的宏。ANSI
C 的宏跟 System V 的宏是不兼容的。
参考文章:
http://jazka.blog.51cto.com/809003/232331/ http://blog.csdn.net/aihao1984/article/details/5953668
相关文章推荐
- Zookeeper启动失败(java.net.BindException: 地址已在使用)
- 安卓系统与API版本对应关系
- 保存到本地的方法
- JdbcTemplate返回Map和做ORM的性能比较
- 靶形数独(codevs 1174)
- php分页的实现
- openstack on centos 7.1(identity)
- 面试纪要
- ListView属性详解 参考3
- Conversion to Dalvik format failed: Unable to execute dex:
- ListView属性详解 参考2
- C++连接Oracle之ADO
- LeetCode-191.Number of 1 Bits
- CentOS7区域设置
- MySQL数据库设置主从同步
- Identity service-ERROR
- avalon框架
- 深入理解Map与Set---用集成HashSet来实现Map
- repo sync失败 remote: warning: suboptimal pack - out of memory
- 处理问题的思路,基于easyui