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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
; a( K U B! F! Z它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
3 o c! J! @% P+ j/ f/ `* _' G可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不- y; z F. e; g$ G
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
% h# M$ b w3 l5 Q; w实际的名称与之相对应.
: {* L4 \( I$ n# s. `: x由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
% g h) I; s) O然而,更多地自由,同样也加大操作上的难度.
( w8 @9 A# @5 M' }+ L* U" e- H以下就对可变参数的几个方面作一定的介绍.- x% \3 Z9 @* [- b9 _
4 f. R* X, m6 a
1)可变参数的存储形式.
8 ^: g8 u1 m2 ]7 O* e% ~
$ W6 k4 t, H: P# G, h: ]. d大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
8 m2 }1 y( j# Q' u0 ~7 r存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
4 ]7 I: }; x( `1 {* ?在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
6 p2 p6 l. V# t- w* J, i这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
1 c" X% p/ D1 X) B因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
' N' M6 t$ I$ q! e. B9 d6 G栈区:
+ \ E; C/ u8 q+ i2 l
- l( L2 u9 @# Z* ^|栈顶 低地址! m. `- K: C O+ _& ~! j# T
" P$ f- \* K8 [6 t& [! C|第一个固定参数var1& V& |, }7 l. [8 L
|可变参数前的第一个固定参数var2
* Y& R0 D( h2 p) W, N8 |+ @ D|可变参数的第一个参数: R x! S8 ?/ I
|...& X, `% g( K5 ^8 O0 a; |& L; f
|可变参数的最后一个参数6 K0 `2 y% j+ y1 C+ e7 e7 h9 ?
|函数的倒数第二个固定参数var3
' W/ e, Q1 b7 d- Z|函数的最后一个固定参数var4
6 c9 L, i5 }1 i7 ?- E, ||...
. x" C* j4 i- R- r: |5 I|函数的返回地址
) D% {% x: |* B& M( L5 M/ p) z|...9 w. M0 v2 ]0 g
|栈底 高地址. P; }& \6 I8 a- @
7 H7 h* S0 ?4 j" @" n$ n) p* h' e
2)使用可变参数所用到头文件和相关宏说明
, t2 s8 H6 L, h7 t6 c Q+ l
* F- K" Z9 ^9 N# Z5 `! j0 t7 x在此,以TC2.0编译器为参考对象来说明.
: t) t. W5 d- h- S可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
0 R: V9 F8 a- ]( Y此文件为:
5 d& c0 U( U. k: I/* stdarg.h
7 t& @- S+ x% I8 I! K/ E$ Y w) M5 U5 J. `" X S
Definitions for ACCESSing parameters in functions that accept
8 @6 s0 L% z9 S" y8 n( Ca variable number of arguments.
~% P! f3 h3 G1 C5 |% G ^6 ^, r& D" O- z
Copyright (c) Borland International 1987,1988
: F- \' j" K' v5 ?All Rights Reserved.2 ? b( R& @8 t+ m3 H
*/: J$ [* b1 g0 }* r, _9 A9 i
#if __STDC__4 `" m$ m) @8 l. b( \. r. o
#define _Cdecl
2 K" ^2 k% n' X# }#else
3 w4 N2 W7 T3 f( i: U: v/ _5 T: `#define _Cdecl cdecl; z: r8 A. J; [3 |3 }5 ?! N
#endif( H$ i9 v1 L# ?! z! q* _
8 {' {4 c' ^; L8 G! w5 `#if !defined(__STDARG) g9 k5 s! a$ S8 _5 @7 Z5 S
#define __STDARG: p$ `. r! |7 T4 |9 }7 D
% J3 f \5 Z" y" Q! ? L; x9 |2 K
typedef void *va_list;; f/ u+ p( J& V' K6 k
# n& ]0 m5 P" _* l, G' Y#define va_start(ap, parmN) (ap = ...)9 }) G; U( \5 `4 P0 Z2 o$ o
#define va_arg(ap, type) (*((type *)(ap))++)$ n! J' \+ @( ]) Z5 W' V: `
#define va_end(ap)
n% h9 o' x# v5 V#define _va_ptr (...)/ A0 ~- K! ?. w7 ^- `
#endif
% s1 B4 j2 x# K$ a: d! d, O3 B6 I: Q4 o i% N
以上为"STDARG.H"的内容.
6 T( C0 k: M( P3 X! B5 T& k" p该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;4 T; r, q1 A0 R8 K1 A/ y+ k9 D
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,* @0 ~9 a8 n2 ?
parmN为可变参数的前面一个固定参数.! W6 G; i" Q$ l% e( o
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
: l; R& W" B, k2 O+ Z5 O' S' pva_end(ap) 结束可变参数获取.
( W" I$ V( e8 c0 i. S, I# l3 |* P
3 r) J6 Y. q; i2 j3)可变参数的使用实例& \ c7 Y. P: t5 O7 i! ]
1 f0 C& e( r% m4 ~& F4 `
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.2 x# E7 E* l" U! {% k) Y- Y
- L1 E$ z9 m6 ^" T% X
#include<stdio.h>$ J) u7 D6 Z# H6 U: b3 i
#include<conio.h>" M# }) @/ X* S) u& R
#include<stdarg.h>3 E4 z6 B. _' ~ |
void tVarArg(int num,...);/*num为可变参数的个数*/
/ M) X1 m' e7 _4 ?' Y0 h$ Aint main(void)
5 O% p) Q4 @$ c6 _: e7 _ g{
8 \7 F" Y T6 Pclrscr();- X- U' y2 `' i' W; x
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 Z* ^& p( D1 B5 a( n
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");& u- x* g1 D8 C$ N" N
getch();
; X: j8 I1 F+ |+ k9 O: H; mreturn 0;
3 v. {& x1 U, q i' s6 }}/ O# B4 O V# W/ T
void tVarArg(int num,...)0 C9 A3 y% f! Q9 ^0 \! i: @
{) o$ W, r2 U: v w
va_list argp; /*定义一个指向可变参数的变量*/
( q8 j9 z5 A& V+ L: ?va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
8 z7 b5 t" V* Dwhile(--num>=0)
^" U! b' ^4 d5 \' }. Z printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
3 k$ V: x- e# v: |- H 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/. o) k- e7 G: A7 R
va_end(argp); /*结束可变参数获取*/
/ @# ^& B6 Q* z9 \return ;5 B: F. c: q+ L( @) ]$ r) \
}
* u* f1 K' U" k, c6 s2 f+ c5 O
/ x" I' `, h* H8 A. B. ^# C4)可变参数的使用需要注意的问题/ ?- J1 l+ e; t$ K
' r1 H# f( v& ~: x0 A1.每个函数的可变参数至多有一个.
F* ^: ]) ^2 ~ w2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数./ @# ?8 o: A! z4 c j7 f& k' o% _
3.可变参数的个数不确定,完全由程序约定.
+ Z! C6 o# |- L" }: Z4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.+ M- O- y7 T( n
而printf()中不是实现了识别参数吗?那是因为函数 5 _! ~& ]# V5 Z
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 2 L6 _/ X6 R1 _2 z6 o
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
4 i4 H+ l8 W" \0 e& H, M" A0 f过在自己的程序里作判断来实现的.
/ n* U7 s% m: \# c( k8 K1 |# m5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|