返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.1 V1 r% L  }5 B3 G
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
( l$ L& G& s4 e: n* U4 q可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不' X' z+ T5 C8 V8 k- X% f
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
, a( s/ `3 Z0 U' h0 y实际的名称与之相对应.
! J0 T. w$ z3 L9 `由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.+ s3 s. O9 X) ?$ D
然而,更多地自由,同样也加大操作上的难度.
8 S8 Y6 q" m, Y4 v5 A: j以下就对可变参数的几个方面作一定的介绍.. D) q8 _0 ?4 Z# U6 D8 s
1 n2 x4 _% ^3 ]! C# `) Y
1)可变参数的存储形式.* a' `  A/ h7 R' @" Y8 k
7 s" O0 H0 z$ z( Q4 v
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,  H  w* I% C4 E. X/ C0 M5 L$ m) g
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.! `8 R1 T! p5 l1 n% D- W) x
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
' k3 i" C6 A9 Y! `' k这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存., ~% _& k3 z& b! j3 h6 y6 I$ v4 N
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
: n" D4 ~2 ], @栈区:
$ ^8 {) Z2 Z+ o% U0 c! w6 F* g2 I$ ^
4 h9 L" y2 U: e|栈顶             低地址
. g' Y+ @: m/ ~  F9 T1 w& ~; \7 @; Z3 P1 W0 t" T( K" a& A! O7 l
|第一个固定参数var1
& s) o- I4 ^8 ]/ w: e: n|可变参数前的第一个固定参数var2
  p: G9 l7 }+ f; Y( F+ V) ~|可变参数的第一个参数
' f  ^" C& l* Y3 |) W2 d* c|...8 C: r1 Q% R% ?$ p
|可变参数的最后一个参数! h# j* l* A+ G5 o' U" H2 o0 J% R
|函数的倒数第二个固定参数var3
4 d. F( X  W4 \3 u" l2 |* a1 N5 i. Q|函数的最后一个固定参数var4
4 V# t6 O- D+ m, {|...
/ r2 x$ F/ c7 P1 H8 v|函数的返回地址$ E1 L/ N' R3 K& }/ J: q1 ]2 @
|...; H, [+ F; y& o0 F) f6 n: d  I
|栈底    高地址2 D8 _( R$ K& i& Z/ K0 X7 U

6 d3 B& s: a! d: D  Q: [4 F2)使用可变参数所用到头文件和相关宏说明, b! {. G0 Z& D! l) O& ~

- C% r- E7 g( P7 f- D在此,以TC2.0编译器为参考对象来说明.
3 v- s" f& J; T6 K% l/ l可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 F* n3 B& n- d. t" U6 h- @
此文件为:
+ M; G; }& u' Z  n& Q* L% C" C/* stdarg.h
6 F( s+ l9 i2 ?: K- [2 u8 @. t4 J5 O( y& l" k6 h7 a; y
Definitions for ACCESSing parameters in functions that accept! D! @  ^5 z2 v
a variable number of arguments.
) ~% Y3 U8 M* w' o4 y
* t, h/ Q. H8 V$ `, y1 a# hCopyright (c) Borland International 1987,1988
( q: t5 u; v/ E& M3 @# x; I. Y7 uAll Rights Reserved.5 {& l/ B# n* q8 E8 i
*/* e- b. z  W9 D+ @% @2 P, A! F
#if __STDC__
5 X( A' I- n$ n, J- k& E#define _Cdecl
' J. _3 M) S3 U. I#else! m+ K, u1 Z% x6 ^
#define _Cdecl cdecl2 q- K2 H6 y+ w/ `' n. k3 R4 Z; w6 ]
#endif
  [4 N& g8 L0 u6 v
5 b9 |) G/ o9 f1 U: I% U1 J#if !defined(__STDARG)8 F. `' x/ T/ I$ y# X  S" g
#define __STDARG
* t' ~6 r5 u) _- }
7 e1 C7 l- K* S* e9 Ttypedef void *va_list;, t& c( u7 @' h6 P5 d- s7 H/ S
/ G3 W1 K0 n- o, N
#define va_start(ap, parmN) (ap = ...)0 |4 C& `3 ]- }9 S% \- k. |
#define va_arg(ap, type) (*((type *)(ap))++)9 D' X1 L  A: P! O
#define va_end(ap)5 V; d4 ~+ @/ N3 z! @2 i
#define _va_ptr   (...)5 O# P" P4 \, Z  o2 _" T
#endif  @8 E! z: @! {) J$ Y7 g" m

( N; ~+ R( z1 x+ }( ~7 d# y: H8 I以上为"STDARG.H"的内容.  b5 p' F* X3 E* h  J$ J9 U
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;$ f0 ?; ~( t9 b6 U1 Q
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,% B2 o' H/ k8 d7 a
parmN为可变参数的前面一个固定参数.4 y' ?. \6 }9 q9 Q! @" U9 B7 `
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.; f4 J$ F) S" H4 n
va_end(ap) 结束可变参数获取.$ G1 `. U; ]3 Z+ K
7 b5 w3 p! q' X) {. ^0 ~
3)可变参数的使用实例
$ S' ]/ Z1 `5 J7 s1 l0 g7 K, |; L% U8 W9 F- }6 I
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.% |' O8 s( D6 B0 N# f$ x' k6 w

* w2 F# d$ i1 ^- I#include<stdio.h>, |6 w- \' P/ a) w5 K
#include<conio.h>5 v4 [  ?0 q* C/ I7 V. y9 b5 }/ E
#include<stdarg.h>- o* z2 s8 N, p* ~3 n( I5 N
void tVarArg(int num,...);/*num为可变参数的个数*/+ E5 h8 T6 N& W( b) ~
int main(void)
/ v; v" _! U3 d$ i( V% I{
) p. y5 k1 M1 Aclrscr();
. ]: K' F9 n: otVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" v; [+ d2 }2 ^5 Q1 A9 w3 R# g
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
; D. }0 ~6 w& C, r1 Z! agetch();
) }) d2 i2 q' j( @6 O8 Jreturn 0;
+ J/ i3 n$ u' u* @}! s. B4 m  K! J
void tVarArg(int num,...)
. V  a0 V( Z6 _* y2 R& M, e# I{& a  I6 l  E0 n! s! ?+ p' I1 G
va_list argp;  /*定义一个指向可变参数的变量*/) y: Z+ b) F% t( X, e, T/ A
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*// ?- H4 n. c7 I8 f6 u
while(--num>=0)
+ T2 a3 k0 u2 P* E3 y  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,$ c; f8 ?1 A/ @
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
0 W8 b/ e2 o: H* Q- Rva_end(argp);  /*结束可变参数获取*/
8 S4 |3 F, B( w0 l) rreturn ;
) H) Y+ I) A' j5 L- g}2 \6 r4 W! v" ^$ {
+ t$ ?0 \- g' C% n- Z: ?/ W
4)可变参数的使用需要注意的问题
5 A8 D" g/ E( c" \/ c8 e/ r' d. z% k) v4 C2 r+ k
1.每个函数的可变参数至多有一个.. y" J; R9 ~4 T6 _% H1 `; k
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
+ R/ o  {+ f% G! i% ]3.可变参数的个数不确定,完全由程序约定.
2 S# J( F. S& V( m* P- R+ s4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.5 A/ H  @+ [3 y5 ?: @4 k6 c
而printf()中不是实现了识别参数吗?那是因为函数 & l; T+ C' U# `% ?/ I0 \3 b( ~8 t, `
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
4 C6 u7 r! X- G! e" W: R; L; r- m的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 , I# M- ^, S+ j4 }. v! i" i+ J
过在自己的程序里作判断来实现的.
8 v- H# `& O9 l3 m/ f5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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