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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.! L3 G8 @2 ?" n4 K; m$ @+ U7 { `
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
Z2 n) b3 {! @3 \7 g/ m可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不* L' }8 S( d- j" e
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
2 ^% d0 \& g- r8 n实际的名称与之相对应.* [, R m$ s4 P8 e) |3 U" g
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
8 w4 F B, l5 Q$ T; s然而,更多地自由,同样也加大操作上的难度.
/ e: L' \$ v) P% a0 k$ ~以下就对可变参数的几个方面作一定的介绍.
0 v1 O3 ~) R2 C' Z/ f: u& P
! J2 j5 |8 J3 `& q1)可变参数的存储形式.) r h3 R' u) ?, d# c
# g2 D7 J. v: t, P8 {1 C
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
7 _* _5 T5 n, v- O7 S3 E1 ^存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
: t: T7 `2 L8 V0 { o: |$ T6 U在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
0 l5 z1 D7 K) B5 n: k这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.2 P" o* y* l% m& [7 } M* q1 P
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
+ a) Z, F' `/ ?- A栈区:
| K% L/ r& m6 ^, A) S, T: Y/ k, U- K# ^7 O9 V
|栈顶 低地址8 q( Y4 d* i' S6 C7 [" Y7 Q
! E( G% q" }6 g9 T% Z|第一个固定参数var1
) V; z7 N2 O6 w+ O0 \|可变参数前的第一个固定参数var2
! A8 A: }9 K; }) G# ]|可变参数的第一个参数
1 l8 a$ i8 t" _1 L|...& A! c2 @: g( v# c4 B4 {7 W# b a
|可变参数的最后一个参数
4 o' F n, b; Q; |4 k. U|函数的倒数第二个固定参数var3
& u$ t* G" e! d. `5 h* O8 T4 N|函数的最后一个固定参数var40 |0 M: U1 t2 e8 n) ` L5 {
|...
7 [- p* K$ Z% |0 J|函数的返回地址; @" f) Q2 k7 I/ f
|...5 \1 w# ?' }2 D* A$ W% W
|栈底 高地址
2 X7 @5 K) w I- P
% K) u' O/ y4 L: D2 a2)使用可变参数所用到头文件和相关宏说明 S! [' F% {% A( q7 h4 d
* K. j. i! I$ F4 n* }4 N. Y在此,以TC2.0编译器为参考对象来说明. L8 K( X8 Y# _: ?' N) K* |
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.9 u" E; E% ]8 r O+ Z: G
此文件为:+ a" `& |7 A/ @# v/ X
/* stdarg.h
4 W- q1 n8 K( Q9 W& [4 g3 q1 y. J+ z
Definitions for ACCESSing parameters in functions that accept* ~. } o, x7 a& P, e
a variable number of arguments.
: g$ J% a! F* |1 Q# w
" ]; V4 V9 N9 s& i6 ]3 M+ e P! XCopyright (c) Borland International 1987,19881 P0 z6 N, R; u
All Rights Reserved.
: o# `: m/ j- e! Z% N7 n$ f*/6 Z! L' X/ n* k7 @% K
#if __STDC__( x: D% k# t1 a; ^$ F7 t
#define _Cdecl, Q* d- D' |; T0 Z( O v
#else
, ^: e+ x& m; W0 K0 s2 ~#define _Cdecl cdecl
$ h5 y- x: J/ o4 v( `) d: w#endif: V2 W3 l1 h% k2 V/ o7 Y
9 r0 o3 J2 ~ e/ F+ I#if !defined(__STDARG)
6 P; v( C: L$ A5 \#define __STDARG; Y. U: h2 w0 }+ M: q4 B
2 \/ [3 ?; G, W
typedef void *va_list;
5 }# y; B& W& d4 T5 O8 u! Y6 B1 n, K: l p
#define va_start(ap, parmN) (ap = ...)
* N# |/ Z/ W6 l" z* E0 ?#define va_arg(ap, type) (*((type *)(ap))++)
- K% d5 D0 s' O3 I#define va_end(ap)
$ P$ j! W$ S; k: A0 G; ?2 ?#define _va_ptr (...)
8 ]9 D+ u$ B& y6 o/ o8 L#endif
" Y7 x8 A- d3 C* ], X1 \3 {) S* y6 R$ `5 c! g
以上为"STDARG.H"的内容.$ B Y2 G* F' M
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
' A* H$ v4 j8 J) Dva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ ~8 \2 ?, `' C' H
parmN为可变参数的前面一个固定参数.
% w7 h* L5 {# j8 c; \va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型., h8 U1 j8 |' V! @5 ]2 l* H' j
va_end(ap) 结束可变参数获取.
+ y: ~4 Q! m. C8 {* [* m9 b( I, {& P, G
3)可变参数的使用实例
/ `; [. H7 C; f m3 U2 I' A7 X
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
6 x& l+ v6 A+ J3 B$ d: i& l" J
" M, S; r. H% `& z( A#include<stdio.h>
+ R( ?; r+ ~- D* @#include<conio.h>2 {' E4 [, v; i
#include<stdarg.h>; H H5 m3 a# r }
void tVarArg(int num,...);/*num为可变参数的个数*/
& S' \9 e/ [, K" d& rint main(void)
2 K* K6 `9 ~7 }{0 Q' L5 ^8 Z5 C) p" V C* u0 f4 a
clrscr();
' ?! D2 C1 r) y I; E- @, } XtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");% s* @ C+ z# p; I( v8 V
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
, C& X5 a6 I) d9 a+ r" {getch();
4 l+ G2 \# G: Breturn 0;
0 g6 F! M. b) d& ~: N7 ~7 S5 V}
) a9 `, b, N7 z9 E2 s9 Dvoid tVarArg(int num,...). P' n) u ]! G( _0 V) M3 U5 ^
{
7 g8 J$ D4 b8 |, u$ q" iva_list argp; /*定义一个指向可变参数的变量*/
- L$ M" m4 ~( x: Wva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/* b. ?0 a7 L! z- o6 Z
while(--num>=0)" d& B4 Z+ l$ M9 z9 m, L! _6 O
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
9 q1 {( Q. h& d 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/5 R1 ?, o, T! y1 Y1 A! K8 T: n# ~
va_end(argp); /*结束可变参数获取*/
2 @$ q$ F/ `' D& _ zreturn ;4 a9 M U: b# t$ ^5 g) @4 C$ E% @. H6 Z
}1 c2 d, W! X% r
; F1 n6 _! Z. s1 _" [4)可变参数的使用需要注意的问题, L* \- |% C4 @7 p, @8 _2 Y. w2 {
6 x; S( k1 P4 {' e( t
1.每个函数的可变参数至多有一个.
9 P1 y2 N, p5 X+ y- B/ |, l) n' V- }2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
9 u( o" D$ L' D* E$ s X3.可变参数的个数不确定,完全由程序约定.
7 R. k9 s+ {' w! l4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.( c' c2 s; G) X, I- `
而printf()中不是实现了识别参数吗?那是因为函数
7 w$ `$ Y% A2 o2 G: Sprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
5 |1 S8 e1 p2 a: t9 Y的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ) z/ V$ i3 b' ^- }8 I# L
过在自己的程序里作判断来实现的.
/ b2 {+ v2 ^5 r8 ?. H% j( w5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|