返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.4 O. t% V, K. v3 D& l& X
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.. v- X$ G, n8 Y0 j) y
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
9 a! I. _# K8 q; ~- R8 ^1 a; U定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有/ M$ K0 r* v# C+ Q
实际的名称与之相对应.
8 v/ B* E- |2 o' l$ Z( Q由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
) N$ {0 i3 v& J4 B* P然而,更多地自由,同样也加大操作上的难度.6 K% ?2 o8 `  ~5 W! m6 a4 J, }
以下就对可变参数的几个方面作一定的介绍.
- `  Q8 f1 r! T6 ^. s/ K  |' F1 ~5 Q; `# H
1)可变参数的存储形式.
9 |9 L- W2 v' W, B, l
2 S  ?7 a( Z# |5 S1 x( b# T大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,, |3 P( \1 E6 r- G  \6 q4 ^
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.. j% W# j4 }  F$ A# X
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
6 T: ^' P' U2 J. |. I3 R2 S这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
( u7 p; J/ z- m* `因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):4 Q: D, J; o8 R
栈区:7 @# d& ]" y  K" D( w1 N8 V

4 ^; Y$ q+ O  n' l- }|栈顶             低地址
$ I- ^( M0 }5 {: ?7 _) b! E. j3 Z( ~2 B8 U# G
|第一个固定参数var1
4 P' ?" Q0 R1 N  s|可变参数前的第一个固定参数var2
5 u& f$ {2 i" e3 |) F! {; \|可变参数的第一个参数
; r1 R% |5 o, F8 z6 \|..." }3 V! U) ?! F0 h6 T8 e0 Z8 q2 g
|可变参数的最后一个参数# e+ q. q% I/ `& l- f8 A' x
|函数的倒数第二个固定参数var3; V. U! D) h) K% O  Z2 I  R
|函数的最后一个固定参数var4
+ V0 I% n/ q9 {. }|...
3 R+ q4 s, F# B" B( a) R+ ]* v3 ^|函数的返回地址
& e' x" {4 E4 J+ b! i|...
# T3 U5 s' x6 L. ?|栈底    高地址  K  p6 S6 w% g, W
# }$ Z# R/ O9 c7 g0 |# ]: m0 p
2)使用可变参数所用到头文件和相关宏说明
: d8 ~/ Q7 {6 `4 N! K! w# _* p) T: a& f) t
在此,以TC2.0编译器为参考对象来说明.% x4 x. r0 M2 f
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
  T- z/ T' F& U' c1 _  C7 l( E' a此文件为:
0 N+ v. @  e  X( L/ G( x7 R4 u/* stdarg.h
0 I* _& t9 s+ X1 Q' P7 W' B' }/ r
Definitions for ACCESSing parameters in functions that accept
" x* M8 h1 V7 B& ea variable number of arguments.% j) I1 S% ]* C' q1 z- O; \6 `! P

& }; r3 X, ?% \6 Q. bCopyright (c) Borland International 1987,1988
* E, Q6 m2 k5 y- {6 DAll Rights Reserved.
' `' [! \& {3 k*/
0 \0 |5 f# w* i2 H4 h: R#if __STDC__
% |6 V! n1 O; {$ K5 _/ g#define _Cdecl9 W) s6 N9 [  Z' |3 B2 m* m
#else: ^( b) c' X: {0 F8 J. @7 g
#define _Cdecl cdecl5 ~1 d- r6 ^+ \5 V9 ?$ P% Z/ B
#endif
- V/ e1 D' U3 i: m! ^
5 R, V( E0 l8 Y: K; f" W, Y& s#if !defined(__STDARG)
( ?1 F# ]; _6 D" m  |) |7 N$ _: ]9 |+ g0 V#define __STDARG) e0 Y5 K8 P' o& `3 u. b

4 B% {- Q" L# h! M* gtypedef void *va_list;
- i5 X& W$ Q8 w- O; v) T. s# g9 U' ^2 S' C/ S- B0 r
#define va_start(ap, parmN) (ap = ...)# K# H$ ^* O( L5 a# n
#define va_arg(ap, type) (*((type *)(ap))++)5 ]# u( l# e. ?+ u. x
#define va_end(ap)% T6 c) _1 H, I4 w  X( `. l
#define _va_ptr   (...)/ q3 {' ^8 `, y& {& _) O* P
#endif8 C( S+ O( p7 w6 b
8 u- o  j9 t: q0 T. A/ x( X9 ^8 p
以上为"STDARG.H"的内容.
- j9 y; H, Z$ y6 F& y该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
% s" Y: f; f7 D' x: F6 Rva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,  ^4 S4 {  R/ _8 k6 m. p8 U1 L
parmN为可变参数的前面一个固定参数.  r/ {1 j( ^9 i- _
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
2 i, i+ m# q* zva_end(ap) 结束可变参数获取.
7 q( [- h% Z# }9 _& V; M) D) Y% F$ h2 k2 A: B$ y% ]3 Y- L
3)可变参数的使用实例
7 x4 q; T- j( s! {; Q& I. g& Q
. R5 T* D% F/ z" v实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
" r$ \  M6 L1 c+ j" m
9 H# G# b6 K( R" ^6 C#include<stdio.h>' t& A- ]# i6 Q5 \) b( G2 Q
#include<conio.h>
( n; k2 M) r9 t( Q1 n" V#include<stdarg.h>7 `$ h. Q9 F: ]: Y! ~
void tVarArg(int num,...);/*num为可变参数的个数*/0 ^2 W7 V1 {* q6 }' ^- e
int main(void)0 ]! B; a0 U9 {3 L. K
{
2 R* }# k" l% R% _. nclrscr();
/ Q- S1 J5 a" ]0 P& z; b+ VtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");9 h+ ^$ E6 ~( Y) Y! {
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
2 J3 x/ l. ?% z5 |1 }getch();
& i% n1 Q4 ~5 X7 _5 Zreturn 0;$ N9 f$ `. i0 Z$ z8 }- n6 s9 C
}
- u5 }9 b7 K* D" bvoid tVarArg(int num,...)2 ^3 S: x: }& U1 l
{
( ]: |2 A, V9 X' uva_list argp;  /*定义一个指向可变参数的变量*/" a- ^# C7 w: ?3 A" h- z0 d+ h2 n
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
5 P* n  E% `: h# T; P$ bwhile(--num>=0), @6 h' H$ t3 k, i- o) R
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
& \9 l( q5 K6 S! M9 f  n    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*// i) c4 e/ W& [' i# u" y4 Z6 C
va_end(argp);  /*结束可变参数获取*/4 {! `* K9 n5 v. _  D
return ;9 L; T0 q& y( [& Q/ v! P& e
}2 h; a; ~6 w  w- s2 o  u" Y

7 P6 `8 L* N0 k+ o; g( Z* h6 V8 @+ O4)可变参数的使用需要注意的问题
# F. U/ s* z/ X7 f: s
! E. ~# ^; I  j2 M* F  b/ E1.每个函数的可变参数至多有一个.
: Q! ^4 e+ A" L" e. s2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
0 R7 @$ }; ?- \8 V3.可变参数的个数不确定,完全由程序约定.1 q, W) ?4 Q# c% K) [6 F
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
# s  E( P9 |0 H5 w4 y, ]而printf()中不是实现了识别参数吗?那是因为函数 7 X/ [) l: g" X$ d3 q7 y' o
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg / F8 w1 ~3 E& M0 m8 t! b0 d* v& }, F1 M
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
0 H7 a1 O& }. M! e/ X过在自己的程序里作判断来实现的. 1 F# {8 ?$ ?6 O% R! P1 J) @
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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