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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
7 _5 u* M. s! i. e, o& s% j% _ A它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.7 i% M/ J6 m4 v
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不. H- V) _3 D" v& k; _5 y; f" l' A8 L
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
: K2 i) J6 M8 e/ ~( q实际的名称与之相对应.
4 V9 b" T9 q9 Y% G" n( r7 n Q由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
! \1 K6 Y# `! f5 k. `+ W然而,更多地自由,同样也加大操作上的难度.
$ p/ B6 P9 m, e以下就对可变参数的几个方面作一定的介绍.
3 Y" n* C* e5 J. }+ w/ [4 g
6 C- M' v v, O2 J% Q1)可变参数的存储形式.
8 R. E. ~. r, B& W+ M9 Z% @- U1 q# L
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,* l' Z+ \- V1 g* |! j
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.- I. R' I) _9 C
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,; B" u5 F5 [1 G$ q4 m
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
5 N4 f2 r. B) s( U因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):6 H T! d& g, L) l: K/ `) l
栈区:( q1 ^ }7 ^, _7 E
' d3 S* Z# [' i2 x
|栈顶 低地址8 k1 D/ w' V# m8 J0 v
1 Y& o# V" f/ U+ D1 f2 e5 ]
|第一个固定参数var1$ v9 a5 ?5 y3 m/ `: D& W
|可变参数前的第一个固定参数var2* i g: l# n$ S& H' \& ~* Q1 I
|可变参数的第一个参数5 O% {% E, g5 x1 K
|...& b" A" r0 S* m
|可变参数的最后一个参数4 |9 Y, ?6 t G+ k* I6 [8 w- K
|函数的倒数第二个固定参数var3
2 u8 @$ d1 r+ ?2 l$ _1 f|函数的最后一个固定参数var4
9 R( J4 Z/ ?+ @' x+ |0 E2 r: ||...& O1 E5 i0 V9 y2 q L
|函数的返回地址
3 S$ o. ~9 y+ @" ^|...
: n. s: e! J7 Y$ p5 I# \4 ^! w|栈底 高地址
3 m6 U+ Y D* J9 f( M
- _/ j. y( {* O$ @. i% g2)使用可变参数所用到头文件和相关宏说明' N8 e9 f; z V- ~2 ~% R) Q
; y; z# W1 h: D9 y! ^! {在此,以TC2.0编译器为参考对象来说明.
/ R! F5 T4 ]5 R可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.4 R* j, j8 U3 _4 v- W: U
此文件为:: O8 g" }, J3 g# j& R
/* stdarg.h8 F7 e O! I8 s; t* D
1 Z! w+ C. r9 g# q/ j. ^( p
Definitions for ACCESSing parameters in functions that accept) U/ D$ V, P+ Z8 @" F: ~
a variable number of arguments.
: u' C+ y, B9 [
( x- t6 h0 C, g+ nCopyright (c) Borland International 1987,19880 n: F7 ]. g5 b) W: r
All Rights Reserved.; P) B% {$ \1 A9 {) O
*/
. O& k1 P {. K$ K! ^: a#if __STDC__
, G' J% l3 B, S( `#define _Cdecl
L2 E$ d7 O$ A8 {" _2 \9 }: {$ N2 o#else& x/ y, X, t# |$ i1 g
#define _Cdecl cdecl. }- Q. K6 H3 E% i5 g8 m
#endif
$ {! h K$ e' B
! R" @) B" i/ L; N! H# S, W& W#if !defined(__STDARG)
! R" b' c1 E8 D$ @1 T#define __STDARG5 d3 U0 b% b8 _
' w" o& E$ P5 dtypedef void *va_list;: w$ J' j4 e8 H1 J
0 u5 a+ ?2 M8 C* n- d2 w) C#define va_start(ap, parmN) (ap = ...)( _5 |( Z% A( w/ l
#define va_arg(ap, type) (*((type *)(ap))++)
, V6 e! f7 T8 q( w/ e- a' v#define va_end(ap)7 \( `* I% g- h$ |6 |* G8 e
#define _va_ptr (...)
8 @3 j4 m, j$ d9 @5 r p V0 D* q9 l9 m2 _#endif
: T& i' j0 s6 n% q
) S5 x8 s9 B. b* X以上为"STDARG.H"的内容.( U- K7 }7 s3 W6 c
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;+ P9 Z1 |* k4 X% ]
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,. w1 s! E% A: x. P
parmN为可变参数的前面一个固定参数.# o" \( H* u* ?, e, c) j" o- t
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.+ K! y* ~8 U3 B* `! t6 V; h; F1 s
va_end(ap) 结束可变参数获取.
6 t* r5 `, E( k. s. x
+ Q: e5 w Z) m5 P9 X2 C3)可变参数的使用实例7 S* {. Q) v9 Y# ^( X- @
1 D4 E% h0 r, F9 J" ~1 M
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.' l, w0 U, v9 j4 o
1 D& `! Z0 h. M1 C' O#include<stdio.h>
/ i) q p- S5 ]#include<conio.h>
9 _) f7 H5 ~) x! A5 ?5 s: q9 g#include<stdarg.h>
+ Q6 { b- [5 R5 B: ?" Zvoid tVarArg(int num,...);/*num为可变参数的个数*/8 Z; j& @7 s9 q. S
int main(void)
& p" N& p+ v* q# [{
" V& l* P) _ j8 o9 l, B) yclrscr();
! D3 N" T. L8 J0 r* H) n# E7 o |tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
6 O; w$ p$ n; ~/ v j0 AtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
, n( H6 u" i; p% p, c* I. rgetch();9 J1 ^: _3 ~7 ~
return 0;
& W6 Q m3 P! u}
3 s4 Z3 R. B& Q: o$ c: ]void tVarArg(int num,...)2 v5 z2 m3 \/ T5 j% {% ?, i
{
0 e7 f# y j$ q1 m* C9 y! |* rva_list argp; /*定义一个指向可变参数的变量*/, z7 q# O6 g0 Z/ {2 {7 M
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
* V6 n1 _9 t$ [* J$ b8 ~2 P4 ?while(--num>=0)" H5 |. S! i$ M' L7 H0 X
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
- `' n5 k1 u p8 p, _ 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/7 p( n/ D1 F( f+ x' C( ^% k
va_end(argp); /*结束可变参数获取*/ ^7 `2 b& ]- u. C, E" W& O
return ; V! }/ ]( P* n4 n0 c! F
}
( n1 j6 r* s2 U X; _* V$ c! T1 ^5 t
' s+ @: c' s3 ]* V7 U: D4)可变参数的使用需要注意的问题5 x1 t7 C: a1 L( f
! i+ {; h6 y) h! u( m- [4 H- V
1.每个函数的可变参数至多有一个.
3 _, t# A; b; ]$ P: [) F* t2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
! {* G0 ` t& {, J. v- n! k- i) V! V3.可变参数的个数不确定,完全由程序约定.
m/ I$ M# I' F* \( l; R: s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
% q, q& q5 T' G# z而printf()中不是实现了识别参数吗?那是因为函数 5 A3 g3 `+ P# P' A
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg / _2 P8 s0 s$ P* \
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
- A9 G( g F1 j5 M过在自己的程序里作判断来实现的.
( w( {/ X6 g. S/ y' X4 Y5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|