  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
6 }+ ]+ g5 S7 x2 r7 }% R它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.; i. G6 h+ m$ W7 g9 y7 j+ }
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
; N* W; T0 E3 E' q1 W定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有: C/ ?2 `9 q/ x Z, F
实际的名称与之相对应.+ y, _! V- ~& _4 i! ?5 I! z/ f
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.( O) D+ j8 n- [7 K
然而,更多地自由,同样也加大操作上的难度. }) j4 d$ O; L
以下就对可变参数的几个方面作一定的介绍.
6 f+ R, K3 U1 r7 }, _# q2 d# E
9 i+ F; |8 b+ H9 I; ^" F1 }1)可变参数的存储形式.' O5 |. t) ]( h! ~3 `
# a% W- P. K3 L) u- z+ V6 }大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,0 F F2 \1 }. J, n$ ~
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
7 r) i8 \# m" D2 Z6 T. _在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
, w4 e' p6 J8 @& W这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
& t0 d4 ]5 A3 Y9 I3 n! X因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
|! Z: W7 u) M栈区:6 O3 H8 F$ g+ |1 S4 n* V
5 G7 D- a+ l2 m8 b: Q
|栈顶 低地址. P2 B( i& O# q! n/ x1 I/ P
5 u! t+ W, f; P2 n K
|第一个固定参数var1
1 Q' c- l' v8 f9 ]: x|可变参数前的第一个固定参数var29 P9 A& S0 ?- Z
|可变参数的第一个参数
- q% J) ^) s# U1 j, a0 T/ ]- v% j|.../ L2 w0 ` ?) C {
|可变参数的最后一个参数/ Y+ Q9 d; b( x
|函数的倒数第二个固定参数var3
/ ]$ E2 q0 H% `( f5 D7 F. M|函数的最后一个固定参数var4
1 m/ [8 ]0 Y& w: E|...$ v% Q4 k1 R, W
|函数的返回地址8 `- c" b. X& s: Z/ Q) d2 f
|...
) j0 e2 M' J) w0 V/ b2 `+ M! E|栈底 高地址
( \2 C. D6 {# y/ C. e, E) e2 l
?8 n# o$ a! o) g8 ~2)使用可变参数所用到头文件和相关宏说明
* s8 b' T7 k4 y9 o2 C' E% ~. o: U( [4 j+ _5 s4 q
在此,以TC2.0编译器为参考对象来说明.
3 q) |; P" g o可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 H# e5 h' T1 j; ]( N! Q
此文件为:
9 L" X( f$ C+ B; _5 K( S/* stdarg.h
7 F) C/ ?/ [6 `. U& j( _, V- l+ }+ H* \) m
Definitions for ACCESSing parameters in functions that accept
! k* g. X! H$ p6 P1 Ua variable number of arguments.
. Z% X% L# s1 i0 N+ r; i9 L0 c; d. i/ f) t. h* `
Copyright (c) Borland International 1987,1988
1 a0 P4 Z2 l) }* C$ C9 o0 MAll Rights Reserved.. N3 i( x; Y8 R) S) Q1 e
*/2 {1 F6 }. h- ^: o2 Z! [/ O4 v3 {
#if __STDC__0 f) R! ?% Q1 v T% N
#define _Cdecl8 P+ ]3 H0 |4 [
#else5 K/ Z5 R( c8 {4 d
#define _Cdecl cdecl
O$ j- x6 y4 B! r# Q" N/ O# U+ B#endif2 G8 F3 \2 q4 w$ }5 A* `0 o, \
5 ]5 D0 A' i$ G+ k5 ^: O9 x6 `#if !defined(__STDARG)# {- P, |; Y: A- f
#define __STDARG
7 L- O5 V. d, ^ W7 F
% K1 T5 F# \7 X% v6 ptypedef void *va_list;
% z# R4 @. e7 I9 ?: j2 x
5 `7 K1 e$ N+ J3 S7 [" N6 [% |2 {2 j#define va_start(ap, parmN) (ap = ...)
* _7 F" _& v0 N#define va_arg(ap, type) (*((type *)(ap))++)+ _: _, E A5 a8 ]
#define va_end(ap)
5 n* q0 M5 ~4 x4 o#define _va_ptr (...)
( i$ u4 K( [6 h2 X- \4 Q7 P; I: ^#endif! f; I7 x8 P; Z' O
; Q% X( O6 R: Z0 s+ \8 R
以上为"STDARG.H"的内容.
1 Z; A @) u; p7 n" N$ {3 L该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
0 X/ `, f0 d2 H! i- u L9 O5 x. eva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
+ J5 p% f& H, F2 x4 O/ q* g: _' ^parmN为可变参数的前面一个固定参数.: H4 o, Z1 j% O3 j5 T
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.* q* e2 Z2 B0 |* ?
va_end(ap) 结束可变参数获取.
7 `. K) q: r z! A6 x, g2 g0 ~( z4 F6 U9 h' f; X$ J/ m
3)可变参数的使用实例) k) I: v! x5 ~3 u6 g
! H9 r1 y2 }( Z, t8 d, N* G实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.* G. a: O4 e' t2 O! ^4 {
: [- @* A5 _/ C0 C# @
#include<stdio.h>
0 w* _# W& z7 X#include<conio.h>
$ M% I. ?1 a1 P2 k% r#include<stdarg.h>
6 D- F5 q9 |& c+ }& B( M: |void tVarArg(int num,...);/*num为可变参数的个数*/
4 O/ @9 u( B+ H# kint main(void)+ o% n* N$ V: B( j. b4 ^: _
{3 G, ~3 u% U8 B1 Z$ c4 N1 m( M
clrscr();
! C8 O z& n$ F6 y2 KtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
4 k. A; w3 H6 w, N3 D7 btVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion"); } z+ E) K c- s7 C
getch();
9 ^: `1 _& ]/ x/ s( l9 ^, q Rreturn 0;1 c$ O& R! R; b1 z3 ^$ T
}4 r' N9 s7 [3 ]
void tVarArg(int num,...)
2 f' A2 \" e% q: M0 W q$ j! U& |8 p{- b" Y& x+ g, Q8 M* g9 {
va_list argp; /*定义一个指向可变参数的变量*/
- l1 V* ^* l& m+ v" l$ Z; yva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
( I+ G% z& | o8 `0 H* `while(--num>=0)
$ Y. g; ?8 d' K a printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数, K) `) t; i7 f) l4 r. _) L# D
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
" W" b' K2 B# l+ H- W; Kva_end(argp); /*结束可变参数获取*/
* A0 O# w& m; h$ ]( Qreturn ;
8 r# |0 l7 [% p+ V}' }# v2 b# s' L9 z
' b6 a; L& X u. H& D1 c3 V9 @: ]4)可变参数的使用需要注意的问题
6 X2 N) A$ h" g6 A; l, G& Q7 j
: a. V T8 G) G+ _2 Z& ]( K1 O1.每个函数的可变参数至多有一个.
- K, z) X9 m5 n; J" {2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.% v" G; p0 j7 M; b, h
3.可变参数的个数不确定,完全由程序约定.
0 E( o* [* q8 @. ~4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.# F/ k% S+ [; l! g1 G
而printf()中不是实现了识别参数吗?那是因为函数
8 X3 w8 D5 F+ Q0 Vprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg % t% Q0 o0 s& u& U( A) d8 Y' H; M
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
C- a) {. n/ I. _- Y( u- [过在自己的程序里作判断来实现的. ' m% [% S o4 \7 v7 Y& A; O
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|