一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

搜索
查看: 4702|回复: 3

C语言中可变参数的函数

[复制链接]
发表于 2019-3-14 18:17 | 显示全部楼层 |阅读模式
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”)
  本文主要介绍va_start和va_end的使用及原理。
  在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解。
  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:
7 R5 v' B. d1 h
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  void foo(...);- I' A2 F' @) M6 t$ V. t( z
  void foo(pARM_list,...);5 Z1 K. }6 j6 v, t4 C
  这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。
8 C- H8 O" ^& k5 b' Q* l0 [$ I, Z- ~; S1 d& B* L* @, [& @* o/ b
2.函数参数的传递原理
  函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
/ \# j. M+ U7 b1 f0 g2 y7 R    void func(int x, float y, char z);# \( c7 x# `9 q% e$ K0 j6 I- L
  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。$ m+ h6 ]3 }' ?6 }& ~6 [- d
  下面是 <stdarg.h> 里面重要的几个宏定义如下:
  `4 V7 E) @  G0 e* S$ u$ u9 q    typedef char* va_list;
$ O0 {; k; }- ]5 S! n/ F    void va_start ( va_list ap, prev_param ); /* ANSI version */
: I$ c9 V) s: x: c) L, L    type va_arg ( va_list ap, type );
' q7 ]+ ]/ c/ i    void va_end ( va_list ap );
9 _; @" [! j) w* \1 `6 A4 G0 G  va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。: U" s. `/ g* O8 q2 L' I' o" ]' X
  <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
, V: C7 y  [5 n/ Z9 t1 r* U  [. ?  <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
, o. K( O. V- P7 G  <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;2 Z9 [" L; a) j& \9 q6 b
  <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。1 N# _$ K. X  z3 {
  例如 int max(int n, ...); 其函数内部应该如此实现:, k, ?7 ]% r3 L
  #include <iostream.h> " c) w8 s( ]9 K
  void fun(int a, ...) ; u) U: Q( q' T
  { 2 c* V% U0 _8 P" \2 b7 g3 I- h, K
    int *temp = &a;
1 k2 k4 \' N# M) _- g    temp++;  //评论中反馈有问题,待改中. L) K' E6 ?" v" }+ V, y" _" l
    for (int i = 0; i < a; ++i)
5 R. N; }! \0 N+ O    {
9 @! _) b, V4 O" V. r. c      cout << *temp << endl; / w  M- ^3 b! W! V
      temp++;
9 J. W" @8 s/ p, X7 u    } # G% v) p, I9 X; ^7 Z5 Y
  }+ t. W+ @( @3 w7 \1 }
  int main()
3 o! B5 p0 j& ?8 U( a  h  { 0 d% ^6 G+ s& B3 d: L
    int a = 1;
2 ^3 f& W4 ?" B- c% V    int b = 2;
# p1 b1 G7 {0 K- V- A7 q    int c = 3;
( y: m1 T4 S9 h) n' _* u- [" {/ k    int d = 4; # N2 |$ m5 q' \6 T0 M3 F
    fun(4, a, b, c, d); ; E8 P) Z. P; l0 g2 p! L, m' z
    system("pause"); 7 d) u( P+ o% |) f
    return 0;
8 x, K$ W: o2 Q: Q  }
  Output:: # f  v3 D6 I2 x
  1 " ?  V$ @; O4 F* Q
  2
% z) }4 C8 @$ N5 k6 @  3 $ W) O3 G8 C/ c1 C2 l" G% p
  4
3:获取省略号指定的参数
- d1 w  O5 {' D  在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码: , j# z7 {$ D+ m1 D2 ^; ]9 D
  void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) 2 Z# y( [& J/ b) m8 ^. u8 ?) I7 I. C
  {
+ G' A$ p. W4 G* n    va_list args; ; J$ f9 q5 X7 G# N  I/ ]) |
    va_start(args, pszFormat); //一定要“...”之前的那个参数2 X$ ~. K7 V8 r$ S  W
    _vsnprintf(pszDest, DestLen, pszFormat, args);
/ k  B8 r" X  {$ J! y; r" X    va_end(args);
3 o2 ]& X" X& q. X( _  }
) i  C  o: ]0 ~# Y 4.演示如何使用参数个数可变的函数,采用ANSI标准形式
8 V) r: Z2 M/ t; D( c8 {  #include 〈stdio.h〉
) ^3 z2 S4 n2 g0 `0 I  #include 〈string.h〉
' ~9 l* c# A% k* s  #include 〈stdarg.h〉 6 i0 P* U( {" w$ L. _0 J
  /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/ ( X  L. Q, `; x+ ^
  int demo( char, ... ); / u$ w+ S7 A) H' k% f+ z0 f
  void main( void )
: p" ^/ {! P+ i! i  {
  X1 n# f; I6 ^& B, o      demo("DEMO", "This", "is", "a", "demo!", ""); 4 h( B5 |! M1 R0 d* H
  } * T" V/ A8 |( S0 Z* P5 h% @
  /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/ , L$ z4 S9 d6 t$ g
  int demo( char msg, ... ) & K, ~5 V/ f, y3 i0 ~- u5 a' z
  { 7 P6 a( _* ]# M6 [" V( n  ^
         /*定义保存函数参数的结构*/6 K! w: {' l* \6 ]$ f; b7 T9 h
      va_list argp; ! I( L& q) |: _3 M& X
      int argno = 0;
; E6 U& d  b: J+ K  a; y% ^      char para; + q2 c9 L0 e  K3 j. y
      /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ - Q" g, w+ p' n2 r% b
      va_start( argp, msg );
  v: E/ e( n( E* X      while (1)
2 y( A' j/ d$ X0 [$ E; s         { ; f' o; e- f& Y( D/ H! K4 Q4 D
            para = va_arg( argp, char);
4 ?4 _* h2 c0 p              if ( strcmp( para, "") == 0 ) * W1 @' }& A" d$ L9 A! D, ?
                   break; $ |  s5 |* h' g( O4 L
              printf("Parameter #%d is: %s\n", argno, para); * A. }8 R* W, f
              argno++;
9 A7 W4 G% F' D! ^    }
& w& r4 P9 ?8 ~  m    va_end( argp ); 0 j$ m7 s& {2 }/ L7 q7 E" M
    /*将argp置为NULL*/* e5 b0 d8 ~1 R% D6 a
    return 0;
5 P8 Q) |. g/ X2 E) X  }: |+ C( o+ m1 d' m
+ I2 K6 S& e5 e2 P6 w+ U
如由不对的地方,非常欢迎给予指导!

/ f0 U9 E& {3 }
0 d; I8 p* h$ z5 g
3 @# n# I& Y! ?0 g* R, J
  h4 ~5 O9 Y, y4 b% J) V/ S; o
: |/ p5 o" R* Q$ R% _- [% y
 楼主| 发表于 2019-3-14 18:17 | 显示全部楼层
回复

使用道具 举报

发表于 2019-3-15 14:45 | 显示全部楼层
已经看不懂了耶
回复

使用道具 举报

发表于 2019-6-6 14:33 来自手机 | 显示全部楼层
看不懂,学习学习
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2025-8-20 09:00 , Processed in 0.044785 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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