一乐电子

一乐电子百科

 找回密码
 立即注册

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 114|回复: 2
收起左侧

C语言中可变参数的函数

[复制链接]
发表于 6 天前 | 显示全部楼层 |阅读模式

注册本网站需使用手机号注册和通过发送手机短信验证码登陆。

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

x
【转】C语言中可变参数的函数(三个点,“...”) C语言中可变参数的函数(三个点,“...”)
  本文主要介绍va_start和va_end的使用及原理。
  在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解。
  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:
9 i% r% ]: U* \6 E$ T4 l' T
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  void foo(...);
2 I! _8 J, c$ K$ I& |. j5 f- o  void foo(pARM_list,...);7 a' Z3 E( O5 z% b4 P, V* r1 l1 z1 i
  这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。5 \- k6 R, j, F; Q5 Y
) w/ n- X8 J2 ]4 f& M  p1 y) F
2.函数参数的传递原理
  函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
4 ]! e0 \5 ?% y: w' ?8 d+ \! {. b    void func(int x, float y, char z);( }7 X$ W/ `" T/ A) F3 B/ i6 Q# s
  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。) Q% \+ m" p) i9 N3 k; k6 _5 H
  下面是 <stdarg.h> 里面重要的几个宏定义如下:
: M- i: q4 n0 |' I* \$ |    typedef char* va_list;
1 w4 t# R7 w- y- `; t0 l& _% J8 [    void va_start ( va_list ap, prev_param ); /* ANSI version */% P/ }- z  F1 J! t. A6 t
    type va_arg ( va_list ap, type ); / n$ L* [1 H- V- _9 o: u  n
    void va_end ( va_list ap );
7 r& `5 s. t$ T- m" |6 ^: b  va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。# l/ b5 c1 l1 \" \0 e7 _4 u
  <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);, }/ Y* O* v' }1 U  H9 r! c8 q
  <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
% Q0 N) a% o+ d. a8 q& @0 c  ]0 |  <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
% U+ E, l3 _+ F( C3 F2 g( E  <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。/ e( _! G; t" G5 N0 u0 ?
  例如 int max(int n, ...); 其函数内部应该如此实现:
( N! h: B, [- n7 i6 S1 r  #include <iostream.h> 8 z: P( U6 i2 Y9 r
  void fun(int a, ...)
6 Y" C4 H0 t6 c! _# H1 C0 X" ]9 t  { 1 \. w' I1 v, z! I+ u( O, |! s
    int *temp = &a;1 l  ^) ^6 h5 A0 c2 Y0 R
    temp++;  //评论中反馈有问题,待改中- E; t  @1 v! T( i" @# r5 X
    for (int i = 0; i < a; ++i) / P% l7 m/ Q7 R; G0 B
    {   k+ t4 Q8 \2 [8 `/ v
      cout << *temp << endl;
* [+ d* ^4 y' s      temp++;
! t7 U3 Q$ J. I2 n    }
2 p+ x2 p, ?: X: b% r  }
: K1 d6 s( ~. U7 O. q& e  int main() ! R  a% i. B* s1 ?6 X( i+ H; z! i1 Z
  {
  J, Q; j) p8 E5 |' q    int a = 1;   S# ^1 k0 {' ~1 k8 }
    int b = 2;
9 G3 n0 z, N& U/ C8 u    int c = 3; . X4 _; {/ L, c( O0 k/ A
    int d = 4; 1 t, M# K: e) i' ~7 r0 O
    fun(4, a, b, c, d); 1 j3 u/ d" C4 N+ V/ _
    system("pause");
  O* n  o0 m, r% y$ i    return 0;
3 i, E! p: q& G) I4 _6 H  }
  Output:: 3 d. X/ x1 ~! C2 e0 W& H( B
  1 " m+ e# q& I# B( H
  2 - E; S; H1 j8 z
  3
9 Z! n) w  x" F2 ?" I. t  4
3:获取省略号指定的参数
& q& N. W" i- g/ J1 z  在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码: ; o* O' m, R9 ^+ I; y9 M
  void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) 7 N  o3 Q6 w7 B5 q# g7 N1 S
  {
% m6 U, \4 e3 c    va_list args; 1 P+ o0 J. r( p8 X1 A8 ]
    va_start(args, pszFormat); //一定要“...”之前的那个参数
2 O; q2 `) @* l& L5 }    _vsnprintf(pszDest, DestLen, pszFormat, args);   V; J# M) V4 ?
    va_end(args);
8 Z( R& @" `' [  }  U% P% T8 r, h+ n( G* c$ G
4.演示如何使用参数个数可变的函数,采用ANSI标准形式
0 r! y- t  l" `8 {6 W% f1 |  #include 〈stdio.h〉 . l- V- C' v1 M7 t! v3 `
  #include 〈string.h〉 % N+ y' ?  g" i
  #include 〈stdarg.h〉
  ~" H  F& z) U9 H1 o6 K  /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
) n: s( V( q& r$ L  int demo( char, ... );
6 J  z" K9 y9 W* {5 n* }7 m: u! ]7 W) t  void main( void ) 8 U, E6 _( X) Y, J  ~; G5 p8 D: h
  { . S0 A) S& j/ {7 w/ J6 a- G* A
      demo("DEMO", "This", "is", "a", "demo!", ""); ; K. l% C2 q" X' y( v
  } 7 g& Z& B$ }4 L5 v
  /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/ 0 c7 T. \; A" W
  int demo( char msg, ... ) 9 z, i  D; M8 I( i! [5 Y1 j
  { " [+ S1 V0 u1 T5 j7 N! F
         /*定义保存函数参数的结构*/( S4 q# O" W# n" m6 h3 n
      va_list argp; 3 h# L  b* O( C' l# A% I9 `
      int argno = 0;
* ?# x4 v7 \. o      char para;
) q* }# F9 b) d" A) B      /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/
. o% f% W" y" b$ a6 O7 J3 L      va_start( argp, msg );
6 N( {1 O! b& C" ~      while (1)
9 s% Y" ?* P/ ~  t         { ) N# M/ E+ i- L: m1 n- L* a
            para = va_arg( argp, char);
8 z0 |9 X5 A7 C$ m0 j, u              if ( strcmp( para, "") == 0 )
. T6 U( d$ `" {7 [                   break;
  S6 ?5 S& X9 _- ~1 S              printf("Parameter #%d is: %s\n", argno, para);
; E& @( E/ Q$ h# t5 B" j5 \5 n              argno++;
# G( o" N( B- _# f+ S* T9 x    } / e' k: k0 f0 ?% ]! f) b" U
    va_end( argp ); , c( C" o9 I! H- d( ^
    /*将argp置为NULL*/# G( O( \+ k' \  v$ b" P6 a1 E
    return 0;
9 @  R, C# S, ^; v  }
) i# Z5 d: w5 Z( @  w$ t9 F3 S
( D! _4 H: F5 d/ p) p5 u
如由不对的地方,非常欢迎给予指导!
2 w" p2 n, A- b
$ v, M0 m2 w5 c# J* [) b" K; v2 z
: |1 ?2 M" S- {% f; |. b+ M) x# {5 Z
; j  Q9 w& a- k4 [7 M3 y- M

2 B0 E( t8 z/ r# J/ ]7 B
 楼主| 发表于 6 天前 | 显示全部楼层
发表于 5 天前 | 显示全部楼层
已经看不懂了耶
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2019-3-20 16:00 , Processed in 0.043757 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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