返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
/ H  I! E+ P% j- J它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.- e* K( I( Q9 Y
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
( l9 C8 x% m1 B4 j; j: s定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
5 E& y( E6 K5 v6 z实际的名称与之相对应." E2 C. O1 m) b5 F( W
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.+ [& L5 S  A0 \) f3 ^
然而,更多地自由,同样也加大操作上的难度.: l4 u7 M: V7 F5 c3 J
以下就对可变参数的几个方面作一定的介绍.9 G/ {" [3 Z7 B- f" ~' `8 w, q
2 n4 U6 H- |6 u& I% B1 s
1)可变参数的存储形式.
. A/ ?5 ]& A: ]( |4 p$ V& t4 i9 t; r% ^& i9 F7 d% a/ o$ N
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
! Q, }9 Q0 k3 Y8 [$ h9 s存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
3 Z1 j( y2 n9 `4 @* }. q4 P在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,: G2 B" I! H, [) G& H1 L
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
! E6 o& `7 X1 W. \2 p因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):7 K2 P  O* L, f2 V& O" b: z
栈区:
0 I" z; q; L( G% R) s2 H0 v! r6 }- n( K1 B
|栈顶             低地址
+ P" n3 z2 y/ Q# E# ~! f
/ a+ N  Z. e9 U/ W" ]. O9 l9 `|第一个固定参数var1- G' [+ l0 |. F& A
|可变参数前的第一个固定参数var27 ^2 B( V2 {- F5 X! n7 f* P
|可变参数的第一个参数
# m7 H* i5 O0 a5 p6 a|...
- }# Y, m8 V, I" ?% i& _|可变参数的最后一个参数. y$ G, F3 Y4 {! f$ j( O& Z
|函数的倒数第二个固定参数var3
+ L3 M4 u; K& ^' f# e9 a|函数的最后一个固定参数var4' d. [2 }9 y: O$ w
|...+ y0 {/ s6 j4 {
|函数的返回地址( A! W9 D; G, ]4 y
|.... @) A% Q+ \) h* X1 K# C  B
|栈底    高地址
# {2 w+ a7 ~2 \
: V4 Q4 s% i6 U/ ?- S2)使用可变参数所用到头文件和相关宏说明
4 t7 ^9 Y. A3 V$ h$ i$ Q/ s* X/ E. P/ [0 ^" G. y* ]
在此,以TC2.0编译器为参考对象来说明.
% b0 m. t+ a- k  ~4 M. a% N, ?1 e可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.$ }3 F2 }8 R# q2 q& j1 C; {
此文件为:$ x3 N0 r! c- Y8 b
/* stdarg.h" N! Y4 y+ P0 D: q

7 F& i& V7 x3 Q/ O7 I) b/ ?0 vDefinitions for ACCESSing parameters in functions that accept
; Z' u0 Q1 E3 h: p& Ga variable number of arguments.% ^7 P3 F" W2 Y  t
/ M4 f8 Z: a$ b) o3 C( V8 N
Copyright (c) Borland International 1987,1988
' T3 W" E1 F$ M0 kAll Rights Reserved.& Y+ S- W  t  I! q- Q5 G! f
*/
8 R3 |" S9 J9 ?! `#if __STDC__* N( x* N/ |7 B2 ?+ j6 K; e
#define _Cdecl
9 N- x1 l/ N  o, c8 d4 W#else
5 o$ p: X2 A% N/ K5 [, U#define _Cdecl cdecl
4 A- B  }( r4 |% a#endif
: ?  Z$ O# u2 a7 a6 K
/ `8 ^+ W- \( u, h4 Y& ~- c#if !defined(__STDARG)6 G, u5 e8 c+ P  |* |% I( E/ v
#define __STDARG6 P: e; F5 o7 L3 Y" y$ p2 i

3 t$ |" `3 {# t5 H; ~- A( @typedef void *va_list;* j$ l9 B' E% N$ w- D5 i) G
7 Q* q0 J" }: \  y* m- e5 C, d
#define va_start(ap, parmN) (ap = ...)
: s* M. D. B$ S. t* ^% H#define va_arg(ap, type) (*((type *)(ap))++)' g! w/ @' I  i) q; F7 u
#define va_end(ap)
) |! }$ A+ K9 V& z! e$ N  h7 K, x#define _va_ptr   (...)
( ?; [! f% ^% c; Y) k8 ^#endif
& M9 D' n9 u. |- b# w9 ^. @
( R2 s4 P# r+ Z. S0 T9 X以上为"STDARG.H"的内容.
- g( v) E1 i; k! I1 `) C# J$ [该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
# z/ q$ s/ i; P* q) pva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,) H' }# W2 B/ [; J- `- P! l) Q" b) t
parmN为可变参数的前面一个固定参数.9 R1 g3 N; R$ C6 w5 N) I7 m% m6 W
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
: N5 G2 m# D5 R* eva_end(ap) 结束可变参数获取.1 W2 u. H- J6 p- F5 [
. X8 t/ C) w! w$ \( N+ [
3)可变参数的使用实例/ P+ z% E  ~: t4 o3 f% r* y3 T+ b
8 x6 Q- P$ a" ~# n' S
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串./ s8 n7 g4 l1 e- G; x

7 s) [. b% i6 n0 R#include<stdio.h>; A9 n4 i$ Y$ }2 c0 n. Y$ J* W
#include<conio.h>0 @) k) x" u, x! o! z; N
#include<stdarg.h>: r) @7 p' m! m( @
void tVarArg(int num,...);/*num为可变参数的个数*/3 `1 t. I' K5 t8 \  m
int main(void)' E5 [" u& \; Y3 P0 E
{
8 D+ z2 q( b. s6 a- {clrscr();! m, t2 [% U6 ^; p+ v
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");2 B9 e; @2 z4 \$ A( L
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");3 r7 v4 {6 T! s( A4 m( Q3 ]
getch();  R1 f: ^0 T  c  o; ~! b
return 0;; R7 f+ L6 N& R1 P+ a3 Y" [" p
}
  W% U% r7 X. H8 P; \& O) Ivoid tVarArg(int num,...)
. l" C: [; i4 R4 D" l  A{6 f# {* p! z% z$ k& e/ Q
va_list argp;  /*定义一个指向可变参数的变量*/
# i% v; K- f$ T  g% X* Lva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/9 n! }( N- P! r  W, ]5 |
while(--num>=0)
8 M8 ]* m! X3 y1 Y: T1 e  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,, M; u$ Q2 x: k. y* Z& X  D
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
# H. t' |: a$ b' k+ jva_end(argp);  /*结束可变参数获取*/
0 z) b- P3 v$ o/ Q9 \4 jreturn ;  W: F$ e# N* @* B4 r! h
}
- z" N& D9 Q/ E- R3 \# T2 `/ ?  ~" {
4)可变参数的使用需要注意的问题
8 N0 J; J2 E3 K7 R, B, {& ~2 q! a/ a+ B0 y
1.每个函数的可变参数至多有一个./ M' R# m) l9 U$ j
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
. ?+ {$ y- G8 l: H* p" L3.可变参数的个数不确定,完全由程序约定.: A4 Q8 l" I( Y; V
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.  a& h" e' g, e$ ?# e8 ^
而printf()中不是实现了识别参数吗?那是因为函数
" j+ ~6 u( r+ ]1 y9 \7 d, @printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
! Z5 f+ g3 M2 K, G" t9 g的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
: E- F) v) z+ G8 N( e( n0 J/ o* i过在自己的程序里作判断来实现的.
. y' h/ v( e; o7 z. Z5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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