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

函数的可变参数详谈

可变参数的英文表示为:variable argument.7 D# R! f" S8 e  s1 H
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.% `1 v; |  N0 k; K  S% J
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不! J. V# G* a% }8 e: O& b1 Y
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
& ]4 t2 t, H' z$ `2 Q实际的名称与之相对应.
: n' f% w9 x" U" L7 v) @0 T由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
4 V$ I$ B( U- ?, R0 C然而,更多地自由,同样也加大操作上的难度.
2 g: T! I2 r* d' ?7 ^3 u1 f以下就对可变参数的几个方面作一定的介绍./ s5 i( ~/ Z" \& |7 G. ]
/ \% x/ X. @/ W: q5 V# B
1)可变参数的存储形式.
$ U4 c- r4 ]; C% I/ y/ k2 G, ~' j% b  }2 D4 R' n6 T4 x3 Z1 J; {$ G
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,# @* h: t4 F& I
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
+ ?% [0 S* k; P2 b在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,+ Y+ W2 Y% t4 Q2 y3 t- D
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
  K( ~; F6 s3 p因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
; a+ z" [* x7 A# [栈区:( S  m; W$ F2 j9 j9 C8 h6 s

) v" Z8 r% u' A8 U9 `! _6 y5 F' _|栈顶             低地址6 N: _7 U' W$ D( S; v7 x; ^( }
$ L+ R, B5 Z# \  O/ S9 L
|第一个固定参数var1. t' h6 a) D, T5 F+ j! F
|可变参数前的第一个固定参数var2
9 @: o2 t5 ?4 k|可变参数的第一个参数" `2 Z- U" }3 S( a( \
|...  e$ _3 s- A8 F# p9 C- H
|可变参数的最后一个参数
) @$ C. l+ W" j* v# ~- d|函数的倒数第二个固定参数var3
% U; z2 r! \5 j  r* A% b) x, O|函数的最后一个固定参数var4* J+ f4 y7 e+ P7 }2 t0 t
|...0 z/ u* g' j5 [8 ]: Z8 s
|函数的返回地址
# A; J/ E$ N/ S7 N! N& ^|...* W% ?3 |2 a% L% M: N8 p1 B
|栈底    高地址
4 {, n* ^) T  s& h1 C$ H! @% S* }) F6 q7 e3 M, a" }
2)使用可变参数所用到头文件和相关宏说明
. g9 x5 S0 ~& ^
6 i" O6 \/ G1 I在此,以TC2.0编译器为参考对象来说明.
0 D& F& Y) l1 q! n7 p可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
1 N) G. t# g; o/ S5 b5 p6 a此文件为:( ]  N' r3 J( }1 E! h2 p
/* stdarg.h1 ]$ M' }, _( E- Y- c- l
0 Q4 |7 h9 \& }$ Q- x, a; S0 n/ u
Definitions for ACCESSing parameters in functions that accept' {9 b' N# i3 D: q1 \/ K
a variable number of arguments.
1 y/ g+ r2 v0 t4 C( F' D* ~+ @) ]
, I7 b$ n3 R* V& d% T$ ACopyright (c) Borland International 1987,19883 c! O; w$ j; J8 T) n  C9 N
All Rights Reserved.: a! M" G' h3 s* h" ^( z0 y9 p* N
*// I" v0 t! i+ t3 e
#if __STDC__: o2 K7 E# J) I5 b1 \0 W# m% d) {
#define _Cdecl* G8 Z' w+ d! x9 ?9 A
#else7 w( u" i! m- \; H: \' f' }
#define _Cdecl cdecl3 Z- E  S' B& p3 ~
#endif
3 X' F9 z, V5 a1 r% ~7 S/ |% t, Y) ?# O# o
#if !defined(__STDARG)" x% i) l& z& i! m
#define __STDARG8 n  C* u( d1 }
% v0 h$ N, Z4 ^  v. p& _
typedef void *va_list;& \( ^) R; Z- A
9 ~! v6 a# G+ B" h
#define va_start(ap, parmN) (ap = ...)
( `  h8 _7 s1 Y1 S. \#define va_arg(ap, type) (*((type *)(ap))++)
: e2 p4 Z8 G/ l: L9 C#define va_end(ap)
" a9 k4 V& Y9 c- ^, y' q#define _va_ptr   (...)
, T6 v) p8 y/ W, B5 u/ `1 W  E- ?* N#endif' Z5 @3 Q* i, t4 O' y) t, U2 X

9 T, b% Q5 h: T6 K  j以上为"STDARG.H"的内容.
2 {" M4 P% \1 g8 V4 o- q4 _该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
2 k2 ?6 s' J- e0 ?( n% Eva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
: \$ k6 t, d  ?5 q' D* XparmN为可变参数的前面一个固定参数.! p1 M9 D; m+ Z2 k, {
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.1 }7 v, m2 T! w0 M" z
va_end(ap) 结束可变参数获取.% Q2 b* P( e6 W9 ~1 G' u) h/ S7 @

- {( l. L2 B: O, C5 `/ v! w* Z3)可变参数的使用实例
, X  n% @0 H/ J( M: \# @. `7 ?/ U3 C2 h3 F: R8 f9 v* P
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.+ `/ l- {/ U8 M3 a2 l& Y/ G) f
; f- A+ a, T7 U' o' ~+ U5 Z
#include<stdio.h>$ Q$ q, x6 D% l0 D6 q; J; O
#include<conio.h>
4 Y" V3 ^3 G" V/ ^7 s#include<stdarg.h>9 I" ]5 p% z) N, e8 W$ _; q
void tVarArg(int num,...);/*num为可变参数的个数*/9 p1 h; ^4 y4 G8 w* o* E# q, H1 \
int main(void)
6 d4 N! t- j& `9 P$ _{; }5 i, i& T1 r0 O, V+ @
clrscr();
7 q2 c  d4 m5 I/ Y+ EtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");2 F: Q9 r  r0 s# g% z3 f- l  G
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");. Z0 s$ Z2 W- L7 F' @0 R7 ^
getch();! m, K- A) y6 o1 o0 e1 }
return 0;! J0 x9 _) T" }6 ~: Y! U# i$ C# d
}
* y# b+ R  P: j9 I1 ~) C: M1 |void tVarArg(int num,...)
- @+ a' \* {% v{6 K$ |" O+ B8 ?; y2 K5 Q
va_list argp;  /*定义一个指向可变参数的变量*/9 L# V# {9 y# q& Z' r3 X8 C
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
- V. M0 P! w9 U! `: mwhile(--num>=0)# g5 w$ r- e! U) S* i6 c0 C: @, [. q
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,! N5 E- t2 Q9 q7 U" _0 h. ]' c' C
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/; Q1 A# M5 C1 Q( j- P
va_end(argp);  /*结束可变参数获取*/8 x4 [5 B7 K/ A4 a# a
return ;2 A8 b0 o  Q7 O$ Y. k; ?
}( p; l1 K* K3 i9 r8 k+ b! P3 O# u

- l* J8 k) ]4 g1 q" G- Y4)可变参数的使用需要注意的问题
; Y- e0 H& v% W8 z8 D) d6 i3 j' @7 B0 O& a- z3 U
1.每个函数的可变参数至多有一个.
6 p. v4 T4 O5 s4 d9 B+ N3 E2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
* O4 s2 b8 l" L* H8 X' M9 K3.可变参数的个数不确定,完全由程序约定.. F1 ]% L- w: u/ A; x1 T' `. A8 p3 _
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.& w! R& i+ D* B* W9 k5 K
而printf()中不是实现了识别参数吗?那是因为函数
% m. K: [! z. G; Qprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 4 }/ K$ G% j3 E; x
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 3 b% b: U. v/ _1 j' t4 G( ?0 s
过在自己的程序里作判断来实现的. + |0 r5 n; V4 s8 c
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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