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

函数的可变参数详谈

可变参数的英文表示为:variable argument.% i& u& c  O$ [! i& g
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.6 ?. b' b1 @1 S- ^; u9 H# E
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不; a! m* N- f4 V: ^9 G
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
  G) h/ Y- [/ e) v; K实际的名称与之相对应.
5 j0 f% f) K7 j. Y由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.) q! x* S1 {* R, B6 M
然而,更多地自由,同样也加大操作上的难度.
) y6 I$ ^6 T8 b% M) O$ j* Y! t以下就对可变参数的几个方面作一定的介绍.4 M/ C; d5 d: P: T
8 C0 v2 M6 j& G, H2 p
1)可变参数的存储形式.
" h- f1 S% K; g) _; I' v
; S+ T% o7 ^8 V" a1 c" j大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
" U9 p1 y1 ^: Y2 ?存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区./ j/ X/ w/ k! M. {+ i& S, x5 j$ h
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,! g  W! P- R# P. f7 r
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
% d6 r& J! P/ t" t5 R* J( G' P因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
1 C5 R: v: \4 d( R2 k/ |栈区:$ i+ j2 l  J" @
, J8 c$ \' [( [2 S
|栈顶             低地址( c3 h$ F0 j3 w. G0 Z

, ?3 ^$ n: K( i& l$ p0 n|第一个固定参数var1) }$ v) j6 f7 @
|可变参数前的第一个固定参数var25 X* f& R/ `3 a: v
|可变参数的第一个参数+ O  V" T: @9 g5 f; ~2 x
|...
, q% T) _0 O! _+ Z|可变参数的最后一个参数  D9 R) g' ~8 `" u! e% T+ X& }
|函数的倒数第二个固定参数var3
" Q7 m- B* h* q6 I; O|函数的最后一个固定参数var4# [+ A9 q- B! s* T
|...% `' u* c2 ?: {! I! B0 {; r
|函数的返回地址
& g$ \2 U( |! o# W2 t( y' B" H|...4 A7 |' p8 a3 T6 |6 ~$ Y
|栈底    高地址  W! V( S) l7 o* D
9 g  ?& H) J5 m/ g
2)使用可变参数所用到头文件和相关宏说明4 B: D" B0 Z$ M
+ {: t9 s5 c+ h5 A( j5 j. `6 r
在此,以TC2.0编译器为参考对象来说明.
; B1 b3 P5 @" Q( J9 I可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.3 O( `5 e( q9 C( y+ K7 _- g8 [
此文件为:
% l; Q# X( ~5 A# t( h8 {$ O/* stdarg.h3 r7 L$ t  U4 \( {9 \
; d3 W$ V0 u2 g5 P1 h) ^# {" @
Definitions for ACCESSing parameters in functions that accept* N# Z! e! V5 W% q6 R1 D
a variable number of arguments.
$ `4 L' v+ M( u0 F. u  h$ R4 }) P5 v7 }( ^' \: Z
Copyright (c) Borland International 1987,1988; @5 z7 h5 w4 Y
All Rights Reserved.
+ q3 a' P* B7 O+ m  Y9 ?1 l4 h*/( `; _/ h  Z) w' K7 C1 Y, `% d
#if __STDC__2 z: v. v$ g8 w( N1 b( [
#define _Cdecl
/ k3 b1 D' u0 U( o#else/ a) V, B9 Q9 X9 ~, ^6 r
#define _Cdecl cdecl8 d. ?3 a! q; c
#endif' R" k  ^6 i; {9 U* I4 L$ g2 n& K3 f0 y
. N- {# h9 s1 o) t' a, t) X
#if !defined(__STDARG)
  t+ N( V6 Q, p# g" ^#define __STDARG
7 C* ^, i6 v0 ]6 E- X! W4 l. w; X  B
typedef void *va_list;9 ~- ^$ M+ g0 F. H# V

# K1 [% F+ r) S0 u#define va_start(ap, parmN) (ap = ...)
; Z5 m4 i' w2 ~/ ^  [: p9 |9 H* W#define va_arg(ap, type) (*((type *)(ap))++)6 D; f) s- t1 J2 M7 Y! r2 J7 E
#define va_end(ap)
9 y1 K" r# K! @: R; P$ t#define _va_ptr   (...)* k0 s6 p& X: U  p  C2 [
#endif3 S, D# g! S$ D3 y4 i

6 i: a8 z7 k7 H+ }$ P以上为"STDARG.H"的内容.
2 H$ [4 U- {: C) C/ u: r& W该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;* q, `3 b* i( H' F1 O3 D
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,, I6 j2 |0 X% H% L1 S: O; i
parmN为可变参数的前面一个固定参数.
$ L0 w5 l3 {* Y0 Y, J0 O5 c( rva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.4 |5 C9 V- H% L& D. t
va_end(ap) 结束可变参数获取.
& K, y; D( U3 O0 n. P; d4 B1 _$ s8 w3 k) K8 U5 a+ K
3)可变参数的使用实例: N- y: W- f; O& R% Z

# m( R- R1 X! y6 Y' a1 U: r实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.! n' N7 d# Z  ?# m

7 m  D1 C, a0 i: q#include<stdio.h>' E/ ^6 \3 s4 [& ?0 I2 K& I9 K
#include<conio.h>" Z$ {+ \9 z3 Q2 ?5 U/ ^* f9 @
#include<stdarg.h>
# x9 P2 r: t, z2 R$ x( rvoid tVarArg(int num,...);/*num为可变参数的个数*/+ C/ \- r0 x" T, F) f1 P0 w% d$ }2 C
int main(void): v2 k8 n; A  }( z* |
{
: O$ Z- ^2 q; j& G' }  k& lclrscr();
" w  h/ k7 y: A. @& qtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
! W  T# i( U( I- I: |tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
7 _; ?* k( G, W# egetch();
# U' e) t9 C& w# G0 s3 l7 q8 vreturn 0;' U; b% @" C& a8 n  ^" U; K4 e
}/ Q2 T5 f5 I6 r  D% A
void tVarArg(int num,...)
6 }, F* j8 Y6 N8 ^8 @{2 @7 U' o: ^, y7 f
va_list argp;  /*定义一个指向可变参数的变量*/- a9 v: W3 J6 j% s+ H
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
( ~( w3 X) l# Uwhile(--num>=0)
9 ~! t( v0 d5 k  y7 ~0 A3 M. e  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
4 h# \3 {% @9 }    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/& ]5 i! I' j& K, J6 E5 B* D6 A
va_end(argp);  /*结束可变参数获取*/  a3 R: y, Y5 e
return ;
5 N! C# J' a+ a0 o% F}
; b9 T' D# P& Q" @
, _8 }' u/ X1 A8 f1 B) A* p4)可变参数的使用需要注意的问题0 F2 B! D$ n+ @1 `# j# [( L) ]

' g/ C2 [& s3 X# }1.每个函数的可变参数至多有一个.
$ e" R1 O$ u! Z, {2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数." g: Z) e% x  A" |# |2 T5 R
3.可变参数的个数不确定,完全由程序约定.
+ ]9 W' t$ c- o8 U& T: ^% U2 F4 S4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换., m+ y3 p% ^$ a) J# h6 k/ Y
而printf()中不是实现了识别参数吗?那是因为函数 4 r' u" l$ j% |/ ]
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
& X# C$ D( f! r9 R3 c的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 # r% m- k  M$ W2 f8 g2 W5 _
过在自己的程序里作判断来实现的.
. w$ j/ Q9 G% o5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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