版主
  
主题
帖子
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理。 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理: : Q! W- b f9 K1 e6 ]& y
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...);
/ J; f# B5 B! j3 v9 p6 X% @) D \ void foo(pARM_list,...);
8 f1 t( G4 k# l4 o3 z" o1 u 这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。8 i6 P! ]6 r, m( a5 Z: p/ A( M: W
: s7 T0 z9 X+ i2.函数参数的传递原理 函数参数是以数据结构:栈的形式存取,从右至左入栈。 首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:4 V5 G0 z) O! p1 L: z
void func(int x, float y, char z);
- D! j) a9 _; `9 y 那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
' }+ Q, x4 i, M. N 下面是 <stdarg.h> 里面重要的几个宏定义如下:$ B$ N4 G6 _: J3 ^1 i) p8 K
typedef char* va_list;
" J& a4 V! h8 \0 w1 N# ~- w6 v void va_start ( va_list ap, prev_param ); /* ANSI version */
r! J" A' @" f, y; c# G [ type va_arg ( va_list ap, type ); 5 k }0 x0 a; C3 N
void va_end ( va_list ap ); % W) t6 r6 \$ i+ b0 B( f$ B
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。9 _3 ^7 j; L D/ i9 p
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);* \, [1 {9 ? P2 x8 Y
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
4 d, o( Z9 a: i4 }8 @! }% H <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
9 c0 d0 h9 }# g3 Q t% d <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
8 L& k) E- I7 t. h. T) p 例如 int max(int n, ...); 其函数内部应该如此实现:
" O$ i4 k c. { s9 o; x #include <iostream.h>
% W+ }4 \/ G. l5 i% D+ F void fun(int a, ...)
5 k+ c( Q0 N& S7 N8 s) J" M* r, } {
# ^$ ?' H; V0 h0 Z( m int *temp = &a;
G$ S+ z$ R" \8 c temp++; //评论中反馈有问题,待改中
2 q; A3 i6 `. j9 ^' ?! Q' z: X for (int i = 0; i < a; ++i)
/ B; ~5 \! Y* M {
1 P3 e! c- R' B/ W8 O: U cout << *temp << endl; ' p* _; | Z2 c4 k/ I3 Y2 I! l
temp++;
2 \ D' f+ n% [ } 4 h& u# u# A6 U* a: B, g9 X
}
: q+ g0 M6 J0 c }1 \0 d int main() ! s) i% v7 N4 K0 m2 K5 i
{
% Y8 J9 v7 H7 ~4 Z/ s int a = 1;
R0 i/ ]1 n5 `" v9 p int b = 2;
5 n/ f' m3 m s# w& h0 X7 _$ F$ ? int c = 3; 5 l& D8 e. r5 H0 p; V& U9 J2 I/ n
int d = 4;
$ `8 C, c" [7 w7 a* E fun(4, a, b, c, d);
& C) B" a9 Z* _2 f system("pause");
4 ~, S3 U. R' R7 Y8 d* \ return 0; 7 D9 G( g8 e0 L5 r5 [
} Output:: * i+ J% I1 N/ B1 c c
1
/ x: A# J( u R% G G- J4 g 2 4 Z$ k* I% F. E- e' I
3 0 z, a$ j7 C' z
4 3:获取省略号指定的参数. _; D: ]& f3 k- b. c, a9 x
在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:
3 J0 A1 @1 V; @% x! v8 C0 O( u% Z void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
! S* e0 ]. |4 i; S7 J {
& a, N. m4 m4 w w va_list args;
0 R; ]" }3 p, r. L2 W9 V4 z va_start(args, pszFormat); //一定要“...”之前的那个参数
* y4 I; V$ G* D; P2 J: `# [ _vsnprintf(pszDest, DestLen, pszFormat, args);
9 n- f/ m! U' G x va_end(args); & _- L; t3 ~& B) `, k- ^ y
}
( f9 d; M$ U4 H5 }5 a 4.演示如何使用参数个数可变的函数,采用ANSI标准形式 , r0 ~3 k! s/ `# D6 e# @! N
#include 〈stdio.h〉 ; _3 x; x3 s B" X
#include 〈string.h〉 ' E n6 f+ w7 _4 O" z
#include 〈stdarg.h〉 - V4 `7 \' L' g% S8 q8 O
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
) W# n( ~7 _: y/ s/ _& B q int demo( char, ... ); 2 l5 M& J, j3 x5 t
void main( void )
- y# g5 I. |& m4 n0 r# A" e { " }: z% v+ A; e- X
demo("DEMO", "This", "is", "a", "demo!", ""); 2 s% q3 _- F+ X9 i$ p
}
3 Z3 G3 B1 f# {% ?9 V" u8 R O /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
+ @, q! \$ }# S: q( m5 y: y int demo( char msg, ... )
; j; s8 Q/ s+ U7 {; i2 i* J/ l/ E {
% P5 o2 w4 P* `# q6 b' H1 r$ E% ^5 z /*定义保存函数参数的结构*/5 e" b7 B+ l! K+ R4 Q
va_list argp;
! G6 Z' I8 d, N# J int argno = 0; ! r* E! s: T" O& J9 W0 T" R
char para; 8 s( C" G0 R/ R' B/ e$ M' b
/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/
4 G) s+ K/ _! x/ a7 x/ F3 O4 t5 C4 Q va_start( argp, msg );
& @% I0 H. K* X2 w, C8 m while (1) / e: u* i' ~4 w. m' k
{
7 c9 C' {3 o# j para = va_arg( argp, char); ! h9 W" C' [ k- k! |
if ( strcmp( para, "") == 0 )
$ V" X8 c" F9 q2 f break;
# ^8 K7 k" \+ Q, {4 j4 G5 ` printf("Parameter #%d is: %s\n", argno, para); 0 ]+ ~! z( B) t7 \8 d3 t
argno++; + x( U/ O/ A2 e$ K; O& r$ @+ }
}
, c' m6 P7 E1 }& c2 C6 Y va_end( argp );
# w' y) C* e' \; V) O3 G /*将argp置为NULL*/, R: R) N/ w$ b" n
return 0; . h) p6 v" b5 N
}
) m% D# j+ t4 ]- @1 T, D. u! B# a F6 P) n0 I3 A. x" M
如由不对的地方,非常欢迎给予指导! + Y: S1 I; Y5 c, F2 Z. K) H5 \ ^& G- a
; Y6 f% j1 [( A( Q7 c
& G' K9 K2 }$ k3 S; ^0 |
9 O/ O6 `+ N5 Q$ z: z3 u3 \, V2 `4 e
|
|