获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.1 K7 z5 M5 e$ F9 a
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
0 }  d8 r5 ]) T* u# Z可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不& J# p7 I* Z5 z3 J) v0 ^/ b5 W
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有- o( D$ W: V; o- o8 L
实际的名称与之相对应.
9 ?2 M7 i( V& |8 h由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
/ _; N* y" ?+ ~然而,更多地自由,同样也加大操作上的难度.9 ]7 m% f  l# `3 G5 \3 W: `
以下就对可变参数的几个方面作一定的介绍.
6 s& x  _6 ?/ a/ i9 w/ ^0 f  F9 S) k8 z/ @
1)可变参数的存储形式.
3 d! _, x- w/ E5 |  w7 M  f7 g: d
5 O$ y7 t8 N5 `# U( d( D- C大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
5 x& R6 x* q8 X: p8 X* @存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
, X' u1 U/ e4 u0 e/ |! e7 a/ j在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,+ }% ]0 K! T2 J* C
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.  ]% d1 c% m  z- {. q
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):6 G/ w: L, ^' E# p. ?& ]
栈区:+ ]- o5 E9 ?2 T* t

3 _* O9 X& D- h2 [: L|栈顶             低地址
6 Y  ]- N+ {% `+ p7 C# U6 U
" n5 y8 D! Q! @% @! O6 M|第一个固定参数var14 H7 q. _/ [3 P" m
|可变参数前的第一个固定参数var2
# e4 L, y  z# p9 v3 q/ y. ^2 p|可变参数的第一个参数( l% W* r0 x( w! b5 h
|...% C9 C. o+ u% Y$ l
|可变参数的最后一个参数
* |/ ]' K$ m0 u( {, W# c|函数的倒数第二个固定参数var3
6 ~, E$ g3 ^5 Z; J5 t/ a2 _; Q|函数的最后一个固定参数var4
2 r9 z5 e+ z3 v  a|...) j4 S: f0 Q3 F% {& @* M
|函数的返回地址
8 Y$ _9 ?. D: i4 X|..." D* K  z. m6 _8 d+ b
|栈底    高地址
# g% p) v" S% X- |% G, N+ u; H7 E) T8 ]2 V9 J; X' h
2)使用可变参数所用到头文件和相关宏说明& n- c1 ~9 G/ W

( D8 G0 O% d5 t4 B在此,以TC2.0编译器为参考对象来说明.  K  x0 f+ X) D& d
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
; T' M+ r7 D8 h: a. Q7 \此文件为:
5 H. W5 U1 w& J9 X, D/* stdarg.h3 N5 ~8 d7 L5 [4 i- X3 g. L, _7 v
! s# [- O  ^8 Q9 d; M
Definitions for ACCESSing parameters in functions that accept) T% s1 i8 Z: m: W% Y
a variable number of arguments.; z6 g: J% U8 b

& U. I3 q7 O* Q: ^9 K' ]- _Copyright (c) Borland International 1987,1988$ \! C/ a* _4 X* O: X
All Rights Reserved.5 v2 m' v  y, L+ n/ j' u
*/
( y5 ~, M! E5 L- Q+ ]#if __STDC__. Z- R7 Y& v3 Y8 h8 @% I
#define _Cdecl% R$ S! b/ N- I) Q  F. \' X
#else
% Q( o, h& g) k4 U& @' J4 D* a#define _Cdecl cdecl
! v/ @, F. n: {6 A2 A#endif
& h4 v; p9 I6 U; u
7 U" Y; I" g* M$ t; `* c) _: R#if !defined(__STDARG)
2 }4 v0 U# Z5 h' f  j: M#define __STDARG
1 J0 k6 r8 M7 D& S6 K
: R  c* `) B; W; ntypedef void *va_list;
+ U. j2 e% m" w1 A" s3 G3 D, k
6 H; G0 X8 I$ f7 z* a* m; [#define va_start(ap, parmN) (ap = ...)1 l6 O4 E/ g, [+ o2 k- x
#define va_arg(ap, type) (*((type *)(ap))++)
; V6 E& j4 B) L  w( j; l#define va_end(ap)% _! A, C! z2 w3 N; c* Y
#define _va_ptr   (...)
/ Y2 c9 |) p! U. m  m; B( E2 h0 c#endif  m$ p8 p+ Y1 q: A( a2 p7 S. R6 V2 ]
% H- i. I9 ~9 c9 M( w( Z
以上为"STDARG.H"的内容.8 ]* Y, J- v& P" `7 \
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
  {/ I5 ]; {/ P2 k4 zva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
7 H  b( d( b: g; I+ ^1 QparmN为可变参数的前面一个固定参数.
4 {4 ~, v3 a9 ^$ S' n: W3 Mva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1 x7 c& f# ]8 s7 l3 G" {
va_end(ap) 结束可变参数获取.
4 l. q0 B" |# W9 D' F( q% `: H, \$ f! u( H, Z+ a
3)可变参数的使用实例4 U6 n3 o7 P% }) Y* a: M( K8 J

  `  \7 z$ B5 N( {实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 @) e2 M) {% L9 E7 d- i: O% Q" f( n' O5 C, p2 s, x  g! r2 Z, @6 w7 i" O- M
#include<stdio.h>
! W' m. D8 C9 m4 K#include<conio.h>
+ u1 _# S3 V3 n; t#include<stdarg.h>
1 n' ~1 r# N& x9 B0 R: I) k* Nvoid tVarArg(int num,...);/*num为可变参数的个数*// N1 M; J3 M. ]9 f1 Z; `9 I
int main(void)% [5 N& i: K. t8 _+ P: N6 v
{( ~3 Z9 j1 m, n; S, x
clrscr();2 c( A' y$ K3 s/ H
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
" o: g# ~9 u) {3 x3 rtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
4 G# w0 ~4 @- I/ E/ y- c  cgetch();3 S% X1 I1 J- Y$ W' l) ~& c# U
return 0;
9 i+ b, c% [) a* R}
$ a) a' j/ a- S6 i% Lvoid tVarArg(int num,...)
+ y& ?: a* \/ W{: }) j3 F. Z. C
va_list argp;  /*定义一个指向可变参数的变量*/
9 }$ W5 h% B5 J+ V) z! W2 ava_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/, B0 C  j8 O1 C, K& f
while(--num>=0)" L9 Z+ R& Q1 _. C4 n8 _
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
( T- h0 {) {/ S( ~. P    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
1 @* P0 b7 }# U3 M5 Nva_end(argp);  /*结束可变参数获取*/# H" M; Z2 ~4 s# c; R6 u
return ;
* J' I* ]- A( U& m3 g6 h: {4 z}( T1 C( s  `+ v5 C$ o& ^; _

) D# z) m2 T4 C9 P9 r& {9 b4)可变参数的使用需要注意的问题! p  T2 }* C6 M5 ~2 x8 {
- u/ P$ U& a; m1 e3 [, {" u
1.每个函数的可变参数至多有一个.8 A% `# J7 e; C6 k1 e; C
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
' J4 }9 `2 }/ r  }/ T3 l3.可变参数的个数不确定,完全由程序约定.
/ T5 k; F- K7 G- A& W4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.$ u0 b+ a7 o: K2 p6 k
而printf()中不是实现了识别参数吗?那是因为函数 % W+ ?$ V/ T" l# s
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg . p. t% J, c+ |+ g+ n: v9 T( R
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
! E* H9 G- X$ m  w$ g过在自己的程序里作判断来实现的. % m$ t) b$ f. R& e
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: