|
  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
函数的可变参数详谈
可变参数的英文表示为:variable argument. ?' |8 T9 n% e& ?
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
" G3 u8 z; _4 Y/ h$ K, k5 s3 D可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
! B$ Y4 s1 y: u- s4 _- _. y定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有5 R8 n7 g! N* m
实际的名称与之相对应.
8 K) x" N- e4 z. G& w; k由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
0 Y2 H7 g. Q5 O5 w- S然而,更多地自由,同样也加大操作上的难度." U) M0 Z! j$ q% V/ {7 U$ q
以下就对可变参数的几个方面作一定的介绍.4 n0 {4 j5 N- b/ x9 H
; ?7 x H2 J% H8 Z7 d1)可变参数的存储形式.; j% {8 o* b% ~8 [
% l$ J# j! E. N$ V; Q' F8 E; O大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
9 [1 {' k% q, k# N存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
7 {; o) D- M# \/ h i7 p在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
- M$ l2 a* q/ V0 P0 M8 u5 ^) g这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.1 t( `# i0 J. f) S" G
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):( i( U" A! [7 {% ]
栈区:8 t' t& k7 @( y9 C9 W# Z
1 v1 y9 b a% Z2 l+ |; T
|栈顶 低地址! B( r, S1 |) o4 V$ ~) U+ Q" C" k
8 |- Y9 v3 g$ m& e# ~5 r6 X|第一个固定参数var1% Z8 }3 G* F& f
|可变参数前的第一个固定参数var2
: }$ m6 F2 E- q/ ||可变参数的第一个参数' S+ v2 |. c( R0 N( Q+ g
|...6 }& c( u4 ?( c3 \ M( n& Y5 t: h" `
|可变参数的最后一个参数
6 O. e! r/ q9 ^|函数的倒数第二个固定参数var3! _' S# f- c5 n3 I4 i4 x
|函数的最后一个固定参数var48 p3 J" t6 h0 ?, ?. ? ^
|...: ^5 H( B) N! @/ X
|函数的返回地址$ }8 ^, z" g) R+ T- c
|...: x& [7 k5 t+ s5 f0 J* W2 W, p
|栈底 高地址. r+ Q1 A. A3 l( f
! J" t. k8 b/ Z# E6 ?# O. x
2)使用可变参数所用到头文件和相关宏说明5 d3 q- B% S ~% K4 N* V8 Z( u
/ S2 D# x0 \/ y8 ~; g
在此,以TC2.0编译器为参考对象来说明.9 u, j- O0 r& _' g) Z3 q6 u8 f/ y
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.5 x" H9 V. Q0 i2 W; S7 G
此文件为:
1 z* c. U- e* {2 T$ K4 O/* stdarg.h2 g, N# ?. S4 R) E w- @" z( d/ h
" D6 c+ s k( ]
Definitions for ACCESSing parameters in functions that accept
* S9 y9 F9 b+ R9 w$ X0 [$ Ra variable number of arguments.
1 Y' ]* A; P+ k, a9 X
# V* ^0 `' P# TCopyright (c) Borland International 1987,1988$ d6 w7 E% [; b5 K
All Rights Reserved.
2 p! u: m. W% Y*/' I1 ?- r5 k4 B) t
#if __STDC__' Y. m( v: j" R) B. v" m2 l
#define _Cdecl
6 x, B$ U7 v* r4 E k2 L#else
! }" ]3 {. _3 V! J, G# v9 {#define _Cdecl cdecl
9 b+ X8 j+ |5 v1 m& v, ]#endif
( [6 i3 ^7 J1 a) j
8 W3 T! n0 @- } e. B; ]#if !defined(__STDARG)
8 v+ P' V; a6 K% z p" F1 j; [7 c#define __STDARG0 u3 k. X9 ~. D. M1 E
5 e% n2 g! C- k9 ]( B5 U! U: X
typedef void *va_list;9 Q a$ k) Z+ p& z9 F) A
$ ?9 h/ ~0 Z7 l, I( C#define va_start(ap, parmN) (ap = ...)+ I) x! [' p Y, u3 K
#define va_arg(ap, type) (*((type *)(ap))++); x7 J# z& E% U% g/ c9 m- h! M
#define va_end(ap)
/ S. g# f4 k3 q#define _va_ptr (...)
0 m5 {! ]: n2 H* I3 T) \' V2 x! ^#endif, r6 R ~9 o4 B& j9 Q
3 H" q) r' V1 A5 t4 i" R: ?$ l0 h( J
以上为"STDARG.H"的内容.
- J* q# c& P$ f. n该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
0 K* x9 ?; j. Q7 a5 Xva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
+ E1 Q( ^" t( h' N* W# LparmN为可变参数的前面一个固定参数.
. ?% P+ f) g. g. S. O! _va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型./ q D7 N9 x. ^ Y
va_end(ap) 结束可变参数获取.
; W1 Z6 E/ T' T! [
6 z- Q2 m* h6 r3)可变参数的使用实例0 d c7 x E& G; A8 r: P& ?
) {* \3 ?) i7 e. r5 }
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 n, q& d' t x( X C1 N( K
4 N6 b! k* j: j#include<stdio.h>4 T6 X. X1 `/ j: `' y
#include<conio.h>6 p$ k/ K, r7 w9 G' \2 Y5 d
#include<stdarg.h>
0 D! F3 l6 D1 B' {; Tvoid tVarArg(int num,...);/*num为可变参数的个数*/
{( ~/ y- I7 H. \% W3 a% b1 L( Lint main(void)6 W {! B. k9 j0 G3 E' N2 j' B
{5 w {; Z* V! D# c" \; Q, A5 i
clrscr();9 _. Y& H1 [9 q) v: t, F
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");. L4 j V- @- `( l
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");# y! N2 c7 O7 b8 G3 |# N
getch();3 ^. w: u! Q( R
return 0;
2 m4 S2 n2 g: b% [7 j4 \# [. M0 n}
& S: I: c2 j, S# cvoid tVarArg(int num,...)) I& i! _$ I+ m3 t+ X; a3 F& `
{2 k8 P1 C% I+ B* [' V5 ^
va_list argp; /*定义一个指向可变参数的变量*/
' e: |5 `4 q. v( F' c6 Nva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
$ b" _$ m5 W4 s( I/ C; V& Cwhile(--num>=0)7 Z8 g3 u z/ T: H
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数," G2 P5 @: @5 A# X$ S
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/! O- G# L+ o7 F6 h) }
va_end(argp); /*结束可变参数获取*/' F4 c" O' k6 e5 Q& m+ v9 \8 c: o
return ;
3 _5 e! m8 _, _% x* E* B/ i/ P3 z}9 N1 h R2 C8 g
' `" T8 @ g+ i! Y0 b
4)可变参数的使用需要注意的问题* ^ { u& D- C& z) Y6 [( ?3 n
3 t9 W# s1 K' N. W0 y
1.每个函数的可变参数至多有一个.
. ~8 Z5 p) ^6 j4 u( K& @" F2 M$ y2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数./ y$ {7 L/ u8 Y; `
3.可变参数的个数不确定,完全由程序约定.$ B. {* ^. O& @
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
6 s+ x! X0 r4 \# c4 e, M而printf()中不是实现了识别参数吗?那是因为函数
$ T0 r J$ q! f7 W1 c$ lprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg - Z( k- b% {) s
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ) C7 S- G, x& a
过在自己的程序里作判断来实现的.
, ^' y) ~: L- }' Q; e, D5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|