版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
|
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理。 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:
9 z" G; C/ [1 F. L1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...);, ^# ?4 o: ?3 j
void foo(parm_list,...);1 ~% `3 U$ r! z1 B/ W, A( e" k- U
这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。
9 q$ `: X8 L L0 x' w N3 R$ |% V+ c1 x3 M4 ?5 \- R+ h
2.函数参数的传递原理 函数参数是以数据结构:栈的形式存取,从右至左入栈。 首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:) [# N9 X* g1 C; e/ m2 A
void func(int x, float y, char z);
' C4 R; Z$ `6 o5 P1 K& `$ q; N 那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
6 G4 P3 K4 ?! ] 下面是 <stdarg.h> 里面重要的几个宏定义如下:2 \( K" n6 ?" W9 a+ X1 B# w5 @
typedef char* va_list;
* a, ?) p& ^( y- E void va_start ( va_list ap, prev_param ); /* ANSI version */- W7 C8 ^5 D# h$ L
type va_arg ( va_list ap, type ); 7 f& T {- R# g
void va_end ( va_list ap ); ' a* s6 b% _) [6 K7 O. O, E
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
! d. t1 ?( @, E <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);$ e# y w- f% z1 e; l. T
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
# i: w' ]2 X- E+ b <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;5 R; J5 v0 |5 d1 P3 T6 q
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
7 L8 ?2 d% }7 C3 O5 X' ` 例如 int max(int n, ...); 其函数内部应该如此实现:4 i1 {( X! w& v" K! P
#include <iostream.h> $ Y, \: m2 e3 q
void fun(int a, ...)
5 a+ E6 \4 C* |) R K0 o { 0 o" I( V0 ]" o$ B
int *temp = &a;9 q2 i; M# T% k: a) U5 V' y/ J
temp++; //评论中反馈有问题,待改中- u* j$ H ? G! X* w
for (int i = 0; i < a; ++i)
; `: J- i3 s& d( F/ `5 q5 K$ U { / f+ a! A5 V( E! b. q5 ?8 X4 }
cout << *temp << endl; ; T8 p& t6 x: `# J- ^
temp++;
2 A3 \8 i: A) h1 O }
" u/ Z! y. q7 ~ }
# [& @7 I, I2 q% H int main() ; H2 O! a9 I" h# \ j4 N
{
# y& c. R7 F3 ]* l P* I int a = 1;
# _+ ?8 G3 g" z int b = 2; 9 k: y+ m# J6 [- R4 w
int c = 3; 4 t0 S- f* R: E% X! n) R) ~
int d = 4;
M o8 \+ Q! \. [! o( B fun(4, a, b, c, d);
, k* V- X6 H5 G; O2 m5 z: B. ] system("pause"); " \: l* c8 M$ R1 w( U8 G7 d
return 0;
( F! ~) j- e, f: z- d } Output:: 6 L, d- ?( [) m5 P" ]
1
& z) s; @: A1 F- |( s C 2 ) [4 n% ]% Y& a0 f3 G6 m) L
3 . a; U7 W" Q3 `! x0 I j6 n% p
4 3:获取省略号指定的参数0 u! M' ]7 b2 i5 Q' b( M" b
在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码: 9 q- h% o- P4 u" O
void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
/ g. i7 L& x3 V. L9 u {
5 i% d$ u8 S) s8 ?- G ~% R2 H; i4 Z va_list args;
1 H- e8 q+ `5 Q8 u8 E va_start(args, pszFormat); //一定要“...”之前的那个参数/ Q. v: Z) N# M! P- n/ F0 Y
_vsnprintf(pszDest, DestLen, pszFormat, args);
/ O2 V& R* z% d5 T1 H _: P7 v% @9 n va_end(args);
1 V( j, ?% }9 ^) | }
F+ q9 d; q" g# ?6 L 4.演示如何使用参数个数可变的函数,采用ANSI标准形式 h! z: }* Y# N- x: Q" Y5 L; W; Q- [
#include 〈stdio.h〉 ; P1 C- I0 t8 {! G. a3 _$ ~
#include 〈string.h〉 ' w" p: e" W5 `& [% o$ R
#include 〈stdarg.h〉 ' m! O/ O, w: b8 I+ N4 ?9 w
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
- r2 o! O% B4 F, c4 `' N int demo( char, ... );
$ B( \+ c9 {, r. C1 e void main( void ) 5 o3 D& ~) |4 M+ u8 m
{ ! E. O7 ^5 G$ @5 f7 M" C
demo("DEMO", "This", "is", "a", "demo!", "");
" a3 d- M" @9 B. G }
" ^& X- H" x/ w% V /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
3 |8 ^2 [9 r q+ ~* O int demo( char msg, ... )
9 e9 {% y$ a" U {
" k; \# E1 x% l$ a/ |$ H. q, N /*定义保存函数参数的结构*/
/ m. @6 X5 Z3 \0 S va_list argp; 0 r1 Q6 T; ^' O/ h+ r
int argno = 0; ' x( m8 C& h3 Z: L. t0 Z1 a- a
char para; 7 X' [, l# z$ o, f3 G! f' @- q# f
/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ # W; L5 D7 d8 r7 L( B8 U1 d
va_start( argp, msg ); ' L, Y0 h c y9 h! f% ]" Z% P
while (1) 5 H0 X- `7 i7 e: x9 C
{
+ X6 D: T3 t" x) b% u/ e para = va_arg( argp, char);
- h* S( w7 M; {- h7 n* [- T: Y if ( strcmp( para, "") == 0 )
% ^0 A) C$ c/ ~$ {9 a/ L$ v9 y break;
% Q1 @4 Y2 J) q printf("Parameter #%d is: %s\n", argno, para); ; T& Q- k! t3 E! {3 k7 K
argno++; 8 N. m7 U) o& ?# L* P8 C1 F
}
3 Q, O8 C# u) u6 F9 d- d: n! a! k: ? va_end( argp );
& ~* w* |8 ]# T1 O4 o$ }/ t/ B /*将argp置为NULL*/' I" Y8 z' _( i" P
return 0;
7 b5 ]% _/ t& W4 w' t/ W7 Y! z }
8 F; j& w# v( I' H% G. X9 _ u
4 M% E$ @, `2 P" t% p 如由不对的地方,非常欢迎给予指导! " w8 x [/ O6 j! s( [
?! `4 @1 I6 m% w1 ]$ ^
' C a1 w! Q! D+ w4 Y x) S% X: ]+ D
7 B9 [- j( |- w1 T+ |. o
|
|