版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
|
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理。 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:
& O9 v2 G" M4 [% f: l1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...);/ P/ w3 u) v5 O! d3 C' d, i
void foo(parm_list,...);
8 z$ }- X6 V8 `+ B) ^! t" K 这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。/ I1 E% b5 a0 d- o* J, K3 d+ T5 |0 c
% f/ C3 m$ t" c% Q. m$ l8 u2.函数参数的传递原理 函数参数是以数据结构:栈的形式存取,从右至左入栈。 首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:" {$ ^% Y- k% t: G# R; F
void func(int x, float y, char z);
$ D5 N) J- X N; D5 t+ Y: B 那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。) C/ f1 j: W6 m' I
下面是 <stdarg.h> 里面重要的几个宏定义如下:
% q% m- l/ N, M( u% ]* Z$ z0 `9 L typedef char* va_list;! _. \2 D* j/ I8 [3 [4 X0 E
void va_start ( va_list ap, prev_param ); /* ANSI version */8 ^5 g3 O' R0 u8 m# G# l
type va_arg ( va_list ap, type ); / D( M# ~$ c0 r, D$ Z- \
void va_end ( va_list ap );
( P: u( r: d8 V, r1 s va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
' s" e) [& k2 z <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
3 B9 w4 I2 x$ B <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;/ H. y8 B% R$ @0 F
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;$ ~2 P6 x- ^ I
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
/ ~* L+ n& _, c% } 例如 int max(int n, ...); 其函数内部应该如此实现:
$ a2 _% ^4 u9 | w5 C$ R #include <iostream.h>
; `5 o/ E* p3 r5 ]) k( ?; I: F void fun(int a, ...) 2 E1 ~! x- S* K ^ w: m l
{ 5 g) g/ ?( t, p0 U9 ^+ Y0 j2 _
int *temp = &a;4 S2 M2 z' p6 W; N4 t( s
temp++; //评论中反馈有问题,待改中
5 I: g2 v* }- x0 K for (int i = 0; i < a; ++i) . R( q$ F$ d9 c0 M2 v) [: D8 b. {8 F
{
0 q( x; J3 q5 _ cout << *temp << endl;
$ G) c; d" J/ s temp++; 9 \5 _* J# s% ^
} 0 K& x% {! t L( W2 X4 h4 M
}
' B: g$ N( v- S2 `" s int main()
4 |2 `, p4 k! y$ k2 h {
& s- \0 P! k9 m int a = 1; 8 ~+ s' ~0 e) s
int b = 2; 6 \ L9 [; l5 z) m4 H
int c = 3; & b3 l1 Q, v8 U2 E6 Z, R" M
int d = 4;
r6 F; |0 s& J( G fun(4, a, b, c, d);
/ t4 N4 U, ^5 M system("pause");
- d- G! [! G8 b* p return 0; ) Q) a' \; F+ o( u
} Output::
( R7 z4 H( m3 w* X4 d 1 + c! O1 N6 Y: v3 @3 e. O' J
2 & y: [( e% Q! Q0 w
3
$ X& [' S" c) X$ g' J9 W 4 3:获取省略号指定的参数
1 l4 s' K3 J6 v4 ^$ _ 在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:
1 z# B: r. l- x* y, e6 y2 S0 x void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) 8 F& h2 m6 l5 K( h& D- N; d
{
, R" u0 t0 x ~1 J9 j3 V va_list args; 7 D* w6 ^5 f. _2 h( E' n; q
va_start(args, pszFormat); //一定要“...”之前的那个参数5 ^0 ^. t: M+ c+ N7 i6 Q. v# \
_vsnprintf(pszDest, DestLen, pszFormat, args);
) C; _- D% {% J+ X! p6 H0 M va_end(args); ) I5 r8 _% t' t+ B0 c$ n' t
}1 S8 G" v1 X( h2 f( X: d6 m7 }( t5 }
4.演示如何使用参数个数可变的函数,采用ANSI标准形式 * a G8 v( V( }, J7 y
#include 〈stdio.h〉
4 L* S( [6 w# v+ f1 T2 z #include 〈string.h〉 # _# I: D1 d; v/ e$ U( F; q
#include 〈stdarg.h〉 ! e8 K" b, L0 s' [
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/ ) J, h/ ^$ P( E/ X; A
int demo( char, ... );
3 |$ F# p7 A6 P& J* `; g void main( void ) : J. A2 ?, s) l* T/ _0 Q; w
{
5 p8 w) B7 K5 W* F6 v2 z demo("DEMO", "This", "is", "a", "demo!", ""); - @! p+ s4 z' J+ @/ _% [; L
} ( a" x- D5 a0 R6 A8 j: a! {) k& D
/*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/ ) s6 R2 u+ I8 E2 n
int demo( char msg, ... ) 2 K+ ]; ]: v( d- N
{
2 G+ e( C7 X0 P6 o /*定义保存函数参数的结构*/
, I2 v# z" j& c- m va_list argp;
# c; O/ K; K3 m int argno = 0; , X) N* r" l) L! v" y" i
char para; ; e: t+ h* }1 h6 F8 t! T# q
/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ 8 a" {4 [2 u& o0 m- u- ]
va_start( argp, msg ); 3 R6 J' D0 c% ~5 w% z
while (1) , n( y: S/ U5 u9 P; Y- z2 x
{ % @4 t! w. C% x+ _& B* f% u2 B% b8 p
para = va_arg( argp, char); / m# A$ \. Y1 k: K+ P/ P* |
if ( strcmp( para, "") == 0 )
$ x- |) `7 v$ d& n4 ~" _ break;
5 J/ v N7 Q& s8 C2 w& C printf("Parameter #%d is: %s\n", argno, para); 4 H# B8 A0 l; H/ h: Y' z
argno++; ) C* Z* p$ \: D. W' D
}
9 M* a9 b) e2 j0 j; j2 V va_end( argp ); X' I; F' o$ A
/*将argp置为NULL*/' @/ J+ ?5 w0 d0 {# H! L3 z! E, D( H5 H
return 0;
1 c& G5 D, q* J4 G, |4 l }
1 C( r* R5 F3 Q. _) X: \8 z" r: x! ^) R8 p+ p+ O' ]
如由不对的地方,非常欢迎给予指导! + ?( j* h0 @# w
8 ?( h& T9 M" }& X2 Z7 U5 a
! G0 f, ~+ S4 M9 O- r, Y
8 r' c; L }4 j& L$ F/ _8 [0 D& ^' W( ~ F
|
|