获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.5 n6 c4 ~7 ^2 w8 C
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
. h6 r5 l! T7 X' F8 X) I$ E可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不8 B2 ^) p+ D4 \: t
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
+ P  t; C$ H, H: X0 \, @7 P实际的名称与之相对应./ B8 \4 C; X$ ^2 e& g  R6 d
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.  F+ J6 @9 i; L& l7 t1 t
然而,更多地自由,同样也加大操作上的难度.3 z+ ^, u  n$ S0 ^
以下就对可变参数的几个方面作一定的介绍.* M. x  F, c! M- c$ q: s
* X& Q3 n, @# |. I
1)可变参数的存储形式.* }8 S3 n" w9 g2 h! Q8 {' s
3 V0 k4 B/ Y, j" t
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
* t! P! J. H+ T; @$ ]存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
- I- Z3 O% q9 @) v+ c: ^) ^! i$ J在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
' u" ~2 N  T8 {5 [4 Z- U这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
# k' e1 \" c8 V+ y, ~- I因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):3 k* ?! ~' q6 y
栈区:
' i) a# N% O% z# y- L7 B# W2 K% X: d5 e9 @" K+ q  `7 R4 k( ~
|栈顶             低地址
; k/ v6 y& |9 L1 ~& b% d
. L& H1 [* G3 `|第一个固定参数var1
2 ?6 t- Q: U+ z4 K: g& x( j|可变参数前的第一个固定参数var2
2 d1 n! i, q4 ?+ C|可变参数的第一个参数8 c+ R! ~) K. i
|...
. p1 ~% v8 {3 u1 A3 m* r# H|可变参数的最后一个参数1 m4 \0 ?+ ~, T5 _. s! d; h% Y
|函数的倒数第二个固定参数var3
/ b/ _/ f! s( @4 K; Y|函数的最后一个固定参数var4
1 o7 M7 p# J. x7 t9 n$ @: C|...( X' [6 w3 K  j- J1 X
|函数的返回地址5 P2 ]& A( q3 L$ }8 I8 P
|...0 ~1 n0 h8 E8 w; I$ G, v7 F
|栈底    高地址5 ]% N$ B( h/ Z- x3 n5 M0 _
0 F1 Y: E  P; F8 ~2 W9 X+ _
2)使用可变参数所用到头文件和相关宏说明
1 T/ C* t- ~! f+ O/ C& _
, Y% [& K# ]6 @. g! c  \在此,以TC2.0编译器为参考对象来说明.. c# L6 N2 p! [
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.% K9 B3 G" f" [$ |! p
此文件为:1 k7 Y2 ?6 e0 h1 [% {: |
/* stdarg.h
" ?1 q: Y( S5 N$ N4 L2 F4 A* R, N3 N- n1 N  C$ i+ E5 _/ z0 o
Definitions for ACCESSing parameters in functions that accept
* r: f" I4 C" J5 {/ [a variable number of arguments.) l. U/ t6 X1 \' ], x7 a

1 F2 n/ U$ R; j0 L2 X6 m5 S: {  A1 vCopyright (c) Borland International 1987,1988
$ K" j' t3 }  L" fAll Rights Reserved.
* k) c& l; n1 C*/3 ]) ~. ?2 P+ r4 i: [
#if __STDC__5 v! @" `8 l9 t4 |1 K  O8 }2 n
#define _Cdecl" |+ N- u( d4 d) f* r0 U! f
#else
8 Q; z. I( e2 k/ a, w4 s+ r#define _Cdecl cdecl) V) T) J8 Q0 c: q
#endif
0 f9 l0 f4 S  P+ h% ?: ^# A  ^% L6 e( I2 p' B5 a
#if !defined(__STDARG)  Q5 S" |0 i; p  X
#define __STDARG
) J" z; B/ x# g1 q) j" G7 @; L. H+ O: r8 ^3 p8 g) A3 z6 Q: l# \
typedef void *va_list;1 n( l/ s# N; [! I" }8 t

2 C8 C# f3 s& J% {#define va_start(ap, parmN) (ap = ...)
6 X4 \2 |  ?, ~" k; E$ w#define va_arg(ap, type) (*((type *)(ap))++)
" l8 b) w2 Z( k# L#define va_end(ap)& e1 f- {, k8 q- ~
#define _va_ptr   (...)4 a1 M. S& v  O
#endif( W) Z  d6 M! X) u/ N

( ?: d, G3 e! @, P8 i4 b" w4 m( O& s以上为"STDARG.H"的内容.
. q, k/ P2 k1 [! P8 P* z7 q6 R该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;+ K1 S: B: h6 m: A+ M+ r
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
7 U9 S3 T. X8 B6 j; ~parmN为可变参数的前面一个固定参数.% }  F4 e1 c% `( m9 q5 P; t: l$ D2 ^
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
4 d( l0 o, |( dva_end(ap) 结束可变参数获取.$ C$ @7 m- g( J

  l) s$ \4 f" T  x$ ?3)可变参数的使用实例
7 r3 p' E( i6 {0 B- c8 s. t' u
7 L) a, p6 U5 T* `! w实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
7 p+ [5 R( M$ A4 V0 |* K9 `+ z' M( P3 j8 e/ U6 V
#include<stdio.h>/ Y  z2 S. M9 S' P1 p
#include<conio.h>- M% z8 [( b' H) [0 S
#include<stdarg.h>
- t! D7 M" K1 J3 a2 cvoid tVarArg(int num,...);/*num为可变参数的个数*/
: Q: I: P8 o7 y9 jint main(void)
; n$ p1 m" f3 p{
' Y2 ?0 N6 z7 V6 Jclrscr();, Z8 n/ x- |1 b3 m& i" Z2 I
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
4 u1 b9 y+ B# C7 e+ N& Z. D# n2 btVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");* S8 K& M, d  M$ w$ S! W
getch();8 p5 J+ N9 Q- Y7 J& R
return 0;$ u5 u4 |  S- P6 O/ w2 S* N8 m
}
7 h' b  d- `3 _, x# Gvoid tVarArg(int num,...)
8 T  l, I& `- z, v: X0 _{' u: Z  T$ X9 S: H8 U$ E2 k
va_list argp;  /*定义一个指向可变参数的变量*/
$ `( f. X; t  Nva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/2 T5 i) l; h- s5 N3 \6 A3 r( D) l
while(--num>=0)/ I6 N0 ?' B4 g' |
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
" r& }  L# S2 X# h    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/5 }# z: F( r! ^) M7 [
va_end(argp);  /*结束可变参数获取*/
' F9 C/ Y' V8 ?0 Nreturn ;
9 B, j2 Y' K# M}* t  F" X/ K( S/ r* F  s$ \8 B

) ~4 z) K5 d! G* X' X0 r7 w; e4)可变参数的使用需要注意的问题$ \& e* H) V9 m. L: o

6 I, A& P7 P2 O1.每个函数的可变参数至多有一个.& U/ l; ~6 a0 _  T5 d4 M
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.4 ?; x4 Q2 z3 f$ v5 Y# b
3.可变参数的个数不确定,完全由程序约定.
2 F! |8 `7 ~& \3 ~* l4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.1 H' N* P! I. p! P! h2 D$ x
而printf()中不是实现了识别参数吗?那是因为函数 ( \; e7 i/ {: d) i& B
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
/ h4 r: }( v" f1 c  K7 s  F6 T的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
: E8 D) B( t$ B3 v过在自己的程序里作判断来实现的.
" l# o4 r) Z; z- F5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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