一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

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

C语言中可变参数的函数

[复制链接]
发表于 2019-3-14 18:17 | 显示全部楼层 |阅读模式
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”)
  本文主要介绍va_start和va_end的使用及原理。
  在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解。
  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:

! p. e4 L6 w% @. j: H' p4 x, S
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  void foo(...);
( Y0 Y: f: F; I/ @  void foo(pARM_list,...);3 j4 K) d! D2 [7 p
  这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。& O7 G" b6 W3 V/ S! X6 y

* P2 g5 @  p; G4 X
2.函数参数的传递原理
  函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
0 @1 y) v: m- h2 Y1 }: u6 L; i  r    void func(int x, float y, char z);
6 b# k0 ^# a9 N2 U$ ~! `  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。5 w, t: F1 G! F, m6 i! @9 M& e
  下面是 <stdarg.h> 里面重要的几个宏定义如下:7 t! s, d5 ^5 L4 v
    typedef char* va_list;# T2 m4 `; ^$ H6 z) E6 ?; `
    void va_start ( va_list ap, prev_param ); /* ANSI version */
5 ^1 r7 ~$ s2 b' {7 h/ J/ ^+ |    type va_arg ( va_list ap, type ); ) T, Q, V7 C: }, {4 `9 l+ F* g
    void va_end ( va_list ap );
& I4 N  }& H& e; D9 R/ o  va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
/ `' T4 P3 e* e$ |- d* T% Z  <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
9 P" X3 [- x( K0 {  <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
* K* ^: f9 ^- z- S4 M4 o  p3 I  <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;0 g0 T4 y7 Y( E. [1 C
  <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
; K0 [) j, O2 t  D& ]. V  例如 int max(int n, ...); 其函数内部应该如此实现:" t7 }+ I/ p' E6 C% l$ t( c
  #include <iostream.h>
$ r/ ?+ @" R' P  void fun(int a, ...)
/ f: l4 I# h# S8 A  S  {
7 t6 O. K* q0 J. \  w    int *temp = &a;" @; F9 D& ~5 k- P1 ]
    temp++;  //评论中反馈有问题,待改中- d/ n: |0 b1 H& T) m  [+ |- V# T
    for (int i = 0; i < a; ++i)
! x7 N% b' b1 s; C$ v    {
/ x. A9 r3 X+ _' e/ {' V      cout << *temp << endl; * B; R$ Y( Z+ L0 T, P
      temp++; , t3 C8 |6 s3 b* O: U+ r) c
    } ! q! T7 R; ~+ j/ m* r
  }
5 g; q4 x3 i) [: \$ ]! ~2 O  int main() 6 F8 _3 r) n, `& r
  { ( D& J! T( }1 R* v
    int a = 1; / Z' L# p; {! r) [* [
    int b = 2; : \; O: V, q' [* r
    int c = 3; * J! j+ q7 a5 z9 M6 K( ^
    int d = 4;
: ]; h3 i3 ?  P, g4 B    fun(4, a, b, c, d);
7 G- w7 t6 l( U5 Q0 L    system("pause"); 8 @( `9 q5 T1 J6 `5 N
    return 0; % w* u* {; N. z
  }
  Output::
7 |1 w! T' C* ]6 a  1 3 N9 X7 x$ o6 N/ m& j9 ^
  2 8 X" q4 y4 j/ t! Z4 e( X9 a
  3
% s( b5 A, X. j, H  4
3:获取省略号指定的参数
  \/ [; E1 b) v: F% @; _8 N/ C  在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:
* T! a8 C* i! W  ?# \% o. E& J7 s: S  void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) 4 V/ E7 N+ T+ `) [* M% F
  {
) i- x7 d1 M+ _    va_list args; / U3 p' y! B# [. p0 p( a
    va_start(args, pszFormat); //一定要“...”之前的那个参数4 k# c7 g* X; G5 G
    _vsnprintf(pszDest, DestLen, pszFormat, args);
' _3 _3 M5 y8 O+ G, t2 k    va_end(args); $ g$ u7 d& q2 ]$ n: \/ b: o& a
  }
  t+ R+ u' U  _ 4.演示如何使用参数个数可变的函数,采用ANSI标准形式
( d( s; g) b- q- d( v  #include 〈stdio.h〉
/ @1 d$ d/ l% m  f% a/ V# A, J  #include 〈string.h〉 5 p2 L; t  y2 s) @- p! ]
  #include 〈stdarg.h〉   ?% Z2 E" K+ z4 Y/ e3 A
  /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/ 4 K! M# i) Z8 |
  int demo( char, ... ); $ i/ p5 e- {2 O( j9 v* }
  void main( void )
# W2 B3 \& x- p! r5 K9 {( f2 g* R5 M  { ( q) n# X3 m6 F: v( M0 x5 ~
      demo("DEMO", "This", "is", "a", "demo!", ""); 0 w+ p) M  s' T) }' j" A1 G' u
  } , E, Z% h0 K! w/ b% _% |, J/ g" O
  /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
% g9 u" {7 M5 W( T& r; E9 m  int demo( char msg, ... ) ( O6 J. c6 Z$ R' q7 j
  {
' C7 p, Z9 Y" M  F         /*定义保存函数参数的结构*/
. `' Q2 c8 k7 }5 @      va_list argp;
' d: f. K  F/ T: W* G      int argno = 0; . Q- v1 y$ a* ?* x: o$ D; J: _
      char para; 4 G* e' N% B$ ?1 }! Z4 k, c6 E' }5 `
      /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ ! ~) y, ^# N* ^, z0 K& w- e0 C" @
      va_start( argp, msg );
  U" y; ~1 L- j; E, O: x      while (1) ( o' h1 y' d# I
         { # d# w1 o: R! d, k) J1 K( G* K
            para = va_arg( argp, char);
( T$ q, F8 l# H0 q2 H2 N              if ( strcmp( para, "") == 0 ) . D& [0 s* _+ I3 t2 @* J: l
                   break;
+ ]2 k3 H) K! j( I5 S$ M6 F9 C              printf("Parameter #%d is: %s\n", argno, para); / n) n6 `: n; a6 |- H1 Y
              argno++;
8 b! i" P# d5 P+ e    } ; u4 a# O  Q6 z) b
    va_end( argp );
$ k% i  y5 u/ Z    /*将argp置为NULL*/2 e- h' C, g- [$ W6 A
    return 0;
8 o1 A  X6 `: g  `( q4 `( N  }4 O: z2 p% o* @' z7 A; q

" ?) y0 H$ N! O
如由不对的地方,非常欢迎给予指导!

- z* P* p, @: j" c# ?; F2 R
3 Z/ r( e; ]9 x: N# N8 T' Q# F3 i' L

+ _$ _4 `  v$ l; j1 ?7 r) `; w- Y. a. S! A
 楼主| 发表于 2019-3-14 18:17 | 显示全部楼层
发表于 2019-3-15 14:45 | 显示全部楼层
已经看不懂了耶
发表于 2019-6-6 14:33 来自手机 | 显示全部楼层
看不懂,学习学习

本版积分规则

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

GMT+8, 2024-5-5 10:47 , Processed in 0.060997 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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