关于C、C++中可变参数的简介——(va_list,va_start,va_arg,va_end)
2012-01-04 20:49
776 查看
由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载。对这种情况,提出了指针参数来解决问题。
如printf()函数,其原型为:
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:
printf( "%d ",i);
printf( "%s ",s);
printf( "the number is %d ,string is:%s ", i, s);
如何实现其功能?
我们需要以下几个宏定义:
(1)va_list
定义了一个指针arg_ptr, 用于指示可选的参数.
(2)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).
(3)va_arg(arg_ptr, type)
返回参数列表中指针arg_ptr所指的参数, 返回类型为type. 并使指针arg_ptr指向参数列表中下一个参数.返回的是可选参数, 不包括固定参数.
(4)va_end(arg_ptr)
清空参数列表, 并置参数指针arg_ptr无效.
(注:va在这里是variable-argument(可变参数)的意思. 这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件)
也需你现在还是不能理解,别着急,现在从一个实例着手.定义这么一个函数,函数的第一个参数是固定的,其余参数是可变的。定义为:
void simple_va_fun(int i,...); 其代码为:
代码运行解释:
(1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.
(2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
(3)然后用va_arg返回第一个可变的参数,并赋值给整数j。va_arg的第二个参数是你要返回的参数的类型,这里是int型. 返回第一个可变参数后arg_ptr指向第二个可变参数,用同样的方法返回并赋值给c,类型为char类型。
(4)最后用va_end宏结束可变参数的获取。
小结:
可变参数的函数原理其实很简单,而va系列是以宏定义来定义的,实 现跟堆栈相关.我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。
附加:
参数在堆栈中分布:
在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的. 总之,函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段. 堆栈中,各个函数的分布情况是倒序的.即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分.参数在堆栈中的分布情况如下:
最后一个参数
倒数第二个参数
...
第一个参数
函数返回地址
函数代码段
如printf()函数,其原型为:
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:
printf( "%d ",i);
printf( "%s ",s);
printf( "the number is %d ,string is:%s ", i, s);
如何实现其功能?
我们需要以下几个宏定义:
(1)va_list
定义了一个指针arg_ptr, 用于指示可选的参数.
(2)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).
(3)va_arg(arg_ptr, type)
返回参数列表中指针arg_ptr所指的参数, 返回类型为type. 并使指针arg_ptr指向参数列表中下一个参数.返回的是可选参数, 不包括固定参数.
(4)va_end(arg_ptr)
清空参数列表, 并置参数指针arg_ptr无效.
(注:va在这里是variable-argument(可变参数)的意思. 这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件)
也需你现在还是不能理解,别着急,现在从一个实例着手.定义这么一个函数,函数的第一个参数是固定的,其余参数是可变的。定义为:
void simple_va_fun(int i,...); 其代码为:
#include <iostream> #include <stdarg.h> using namespace std; void simple_va_fun(int i,...); int main(int argc,char *argv[]) { simple_va_fun(100); simple_va_fun(100,200); simple_va_fun(100,200,'a'); return 0; } void simple_va_fun(int i,...) { va_list arg_ptr; //定义可变参数指针 va_start(arg_ptr,i); // i为最后一个固定参数 int j=va_arg(arg_ptr,int); //返回第一个可变参数,类型为int char c=va_arg(arg_ptr,char); //返回第二个可变参数,类型为char va_end(arg_ptr); // 清空参数指针 printf( "%d %d %c\n",i,j,c); return; }
代码运行解释:
(1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.
(2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
(3)然后用va_arg返回第一个可变的参数,并赋值给整数j。va_arg的第二个参数是你要返回的参数的类型,这里是int型. 返回第一个可变参数后arg_ptr指向第二个可变参数,用同样的方法返回并赋值给c,类型为char类型。
(4)最后用va_end宏结束可变参数的获取。
小结:
可变参数的函数原理其实很简单,而va系列是以宏定义来定义的,实 现跟堆栈相关.我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。
附加:
参数在堆栈中分布:
在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的. 总之,函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段. 堆栈中,各个函数的分布情况是倒序的.即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分.参数在堆栈中的分布情况如下:
最后一个参数
倒数第二个参数
...
第一个参数
函数返回地址
函数代码段
相关文章推荐
- 关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)
- 【转】C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)
- C,C++中使用可变参数[va_list、va_start(),va_end()和va_arg()]
- C/C++中va_list,va_start,va_arg,va_end等可变参数宏的使用
- C语言中的可变参数:va_list ,va_start,va_arg,va_end
- C++可变参数列表处理宏va_list、va_start、va_end的使用
- 《C语言接口与实现》实验——可变参数表的使用(va_list, va_start, va_arg, va_end)
- C语言中可变参数的用法——va_list、va_start、va_arg、va_end参数定义
- C++可变参数编程,va_start va_arg va_end 的使用和原理
- apue读书笔记【一】:处理可变参数(1) va_list 、va_arg、 va_start、 va_end
- 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)
- 关于可变参数中需要用到的宏va_start,va_arg,va_end
- C语言中可变参数的用法——va_list、va_start、va_arg、va_end参数定义
- 130.可变参数或不定参数(va_list、va_start、va_arg、va_end)
- va_start,va_arg,va_end,va_list应用举例--实现可变参数的函数
- 可变参数列表(va_list,va_arg,va_copy,va_start,va_end)
- 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法
- C语言中可变参数的用法——va_list、va_start、va_arg、va_end参数定义
- C语言可变参数--va_list、va_start、va_arg、va_end
- C语言中可变参数的用法——va_list、va_start、va_arg、va_end参数定义