一乐电子

一乐电子百科

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录或注册

手机号码,快捷登录

搜索
查看: 324|回复: 3
收起左侧

C语言中可变参数的函数

[复制链接]
发表于 2019-3-14 18:17 | 显示全部楼层 |阅读模式

注册本网站需通过微信扫码注册和通过发送手机短信验证。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”)
  本文主要介绍va_start和va_end的使用及原理。
  在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解。
  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:
% z6 P- J& B  {% s. T. E1 A
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  void foo(...);
4 L$ W, G: w& n! ~0 P# H* j1 T" }  void foo(pARM_list,...);$ L2 Y8 p9 v3 I& }' D6 A
  这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。0 E: N. o: s/ i2 F
3 n6 |+ k$ N& w2 B( k
2.函数参数的传递原理
  函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:! M8 _+ B5 b2 A* l9 V" V% O* b
    void func(int x, float y, char z);
: L! g, _) J" E' h6 n5 H  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
. ^5 m. V; A( h! s' {" N  下面是 <stdarg.h> 里面重要的几个宏定义如下:# A  T( I: _6 n1 ^) T. ?
    typedef char* va_list;+ Z$ n$ Z  L* E' [) }
    void va_start ( va_list ap, prev_param ); /* ANSI version */
/ p! ~1 r; F* k2 k, I    type va_arg ( va_list ap, type ); 0 V8 I0 q* g4 N& I$ U4 d
    void va_end ( va_list ap );
) Q8 N0 O! X3 r  va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。( q* X4 h9 m9 R
  <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);: [" ?, ?  d0 y% u0 K
  <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
! f- w5 l$ k9 P& ]" v' C  <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;8 _7 D6 M+ }( Y% `: l- n
  <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
: h( ]- F0 z8 I) d0 v) f. T  例如 int max(int n, ...); 其函数内部应该如此实现:
/ g; f' T0 k2 X; l  #include <iostream.h>
3 y7 W+ p8 r8 r& Y  b; k  void fun(int a, ...)   N2 u  M  c7 q# A- Y5 D$ ~3 b) t
  { 2 e9 M1 ~' y% C/ v6 p# p
    int *temp = &a;& B- M& E+ d" L1 e4 ]% \: {$ Z; j
    temp++;  //评论中反馈有问题,待改中
  d- z9 `! I& `& d2 h# k1 {- r
    for (int i = 0; i < a; ++i)
* f$ f# u3 J$ c' c0 x& @    {
% _7 H$ o' b( C; W$ c& D' l      cout << *temp << endl;
7 q( P: _4 I/ U! s      temp++; 1 ^/ [( o$ U0 ^
    } 2 V" l2 u' x  t- c" ^
  }
  Y. I  ?' C4 n  int main()
" f; f. a- Y7 `/ G  { 8 M! r( a. C& V4 N, V
    int a = 1; 0 m8 j* k% p! o
    int b = 2; 1 F7 u: r: u1 b& u) h3 u
    int c = 3; ) _6 m4 O: X) [. J2 x
    int d = 4;
) U- m. W8 h/ L/ l; f    fun(4, a, b, c, d);
. ^4 y, P) E; x) m8 q: L: N    system("pause");
+ c) o" m  @6 U' C9 B: L8 r1 N    return 0; & [9 R$ L( ^8 E9 }8 m
  }
  Output:: ( y2 M( `/ O& D0 V* ^7 ]
  1 $ P" z/ Y6 d1 x' V
  2 3 i; j9 }3 t  C6 _7 Q+ U$ R1 F
  3 3 x% i& ^- O5 K0 }0 _5 _8 [; X5 }1 d
  4
3:获取省略号指定的参数% w) M4 h% |9 n9 S6 E( z& [7 m. p
  在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码: 1 I7 V3 e8 ^( S: q0 r" P& Z
  void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) 0 Y  \& a- i- ?4 H& U  f& N
  { 3 o: G! O- P/ B6 B5 Z
    va_list args;
3 E, ]* U& C2 P' X% p2 x    va_start(args, pszFormat); //一定要“...”之前的那个参数! Z. K2 e7 M0 b% X/ U
    _vsnprintf(pszDest, DestLen, pszFormat, args);
1 ~8 k0 R( [8 \' O+ R2 H) s0 f    va_end(args);
" K8 m7 D& A/ U* {  }6 [% J/ a! D' a1 C
4.演示如何使用参数个数可变的函数,采用ANSI标准形式
- d/ S& R1 z! m- z+ g- w  #include 〈stdio.h〉
, p1 p# Z9 c/ S! B  #include 〈string.h〉 7 t, B* n) E5 q5 W
  #include 〈stdarg.h〉
, r+ I" c' x, W, [8 A  /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/ ( M3 g, C1 _/ f3 b: e
  int demo( char, ... ); 9 ?% @: i- v, |: Z* U
  void main( void )
- P& r4 t' x5 s  { " `6 v$ j6 w8 |8 i0 v8 `( ?
      demo("DEMO", "This", "is", "a", "demo!", ""); ' Q3 m- {" ]( O; j
  } " y0 r. @  p2 P& }
  /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
3 ^# r+ D0 f+ P7 ]8 ~6 |  int demo( char msg, ... ) 9 D' M; x& C* ~( i
  { & i; S+ s8 T3 P! e
         /*定义保存函数参数的结构*/
4 N* a' U+ U+ P5 @2 m      va_list argp; / f. t; P4 O& `
      int argno = 0; 9 Z& e" d% i, y
      char para;
  h. z. S: W6 S% G- l" O) {      /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ 2 k0 Y/ y$ w% L# w+ a9 @9 p. L& C
      va_start( argp, msg ); 6 L5 Y$ P3 {7 l  J2 y
      while (1) 9 F. _& u4 y1 O! x( o, w9 M
         { 1 a) T6 C& X! v: z/ [3 t' |! S
            para = va_arg( argp, char);
  E, T" a7 `: ^- {7 @4 x  h              if ( strcmp( para, "") == 0 )
- w1 r+ w" |0 v8 K4 h                   break;
5 }, o. Q' X* |1 u7 y  p, L( Z              printf("Parameter #%d is: %s\n", argno, para);
9 n3 M- t3 l8 x* e* N: G3 t              argno++; . O7 U) o+ \1 d% m
    }
0 B; C- T( O  }! ]' J$ J    va_end( argp );
, |; N7 D$ u, _, d* {$ [    /*将argp置为NULL*/4 H4 S2 y+ @# q, c  E0 |# ?* I
    return 0; / j; R8 O8 J# P: Q: a+ ~
  }. A& j9 K: _0 E3 s
6 J8 B% S. p+ R  a. N6 i
如由不对的地方,非常欢迎给予指导!

( X9 }3 `2 {1 B* N5 r1 z
% C1 F; {& ^/ U8 n7 ~: ~* a4 x. L, G4 S$ Z4 ^6 ]
' v( a: t6 ~% |4 C0 Y; l+ s# {

, j* j. L5 i, i/ g+ ?4 P3 Y1 \$ e
 楼主| 发表于 2019-3-14 18:17 | 显示全部楼层
发表于 2019-3-15 14:45 | 显示全部楼层
已经看不懂了耶
发表于 2019-6-6 14:33 来自手机 | 显示全部楼层
看不懂,学习学习
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|一乐电子百科 ( 粤ICP备09076165号-1粤公网安备44522102000183号 )

GMT+8, 2019-6-19 23:36 , Processed in 0.077949 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表