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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.' ~/ A6 ?6 Q2 l
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
- M- X8 T7 X5 Q7 c8 ]可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不) O' T' b# ~, }$ g6 k$ ?
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
* b5 c& m0 G+ w; ^实际的名称与之相对应.
3 [8 a0 v* `! v由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.% | c. Q9 m7 U0 |% h8 s
然而,更多地自由,同样也加大操作上的难度.
5 e; S S: y) c$ }; O以下就对可变参数的几个方面作一定的介绍.. {/ q$ X7 @6 U v) W
2 p7 F, t- a6 `, m* ]* l) N- p5 A* b1)可变参数的存储形式.
; ]; b+ U6 L9 {5 N. t# y6 z5 q4 U B) w) _
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,( y! C$ x7 L; o$ h
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
3 x- k( }, O3 X在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
+ @. ~2 P# x9 h) H0 e这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.; u' ?+ d% B7 n; x) l
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):! d! f3 M" q+ |
栈区:0 d0 K% f- a6 q
5 X+ v, Y! k& Q4 J( b|栈顶 低地址
9 A* w+ T M( v6 a5 F
" r7 }" P/ o7 D$ @|第一个固定参数var1
) P! L) I' o0 z5 ^3 W( R" h# i|可变参数前的第一个固定参数var2 c1 [( _; m6 [/ _
|可变参数的第一个参数
+ ~9 k# b0 z! M& y* A|...
9 v' _- M( f# y$ G# b|可变参数的最后一个参数* l) H, s0 E+ @8 o% }- E
|函数的倒数第二个固定参数var3
1 ] ?$ @3 ]1 z4 V2 C: W|函数的最后一个固定参数var4
0 F+ T9 k, i& l8 Y* S|...
" p; q# ?+ [5 ^. ?( u* {|函数的返回地址, g+ ~& d9 ]. o2 j+ T* u( Y
|...5 ]4 I, J, A9 `' o! W9 `
|栈底 高地址8 H8 \% ?' Y; p
6 `6 r( G" g. L, c B* _+ D/ f2)使用可变参数所用到头文件和相关宏说明: y6 a' z; \+ a9 g5 s; \& c2 D. S
1 W* z$ c" K0 X9 q; e0 r) J
在此,以TC2.0编译器为参考对象来说明., {! l# P; s6 q% `
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
7 y" V7 x$ E$ g- l, O) b/ t- T此文件为:1 m4 D" i5 {7 a8 j# c* _
/* stdarg.h
& k& O8 _* Y% {6 y- W8 B1 }& t4 ?, v2 `8 o& Q
Definitions for ACCESSing parameters in functions that accept
6 b1 _7 T4 B0 }0 z: w+ wa variable number of arguments.2 [, v1 y; H4 h* k
! ~$ B$ U" O# q4 j
Copyright (c) Borland International 1987,1988
8 |! l( {% { J( ]/ @All Rights Reserved. _2 y8 _# {; G+ C/ s* @- N7 e0 G
*/& ?5 @* ?; U7 t/ `; l( B9 y5 `
#if __STDC__: f9 z& ]+ ~6 ?' U3 o8 F4 ~
#define _Cdecl+ m1 y; E; p: G/ ?$ K# {3 A
#else
9 a' g; V O( C" \' R$ f/ V" x9 k3 v#define _Cdecl cdecl& g+ b# f) n0 Y. Y6 Z4 B
#endif
, j7 d6 i8 F% p1 b- L7 Q! q/ B
% S$ k- B" S+ C( O* o: x8 B#if !defined(__STDARG)
5 _0 @3 s- S0 T* v, e2 X0 w. I) |; J#define __STDARG
$ `( F# B- L' c- v; p0 \: p' H% `+ P; _7 Y! ]
typedef void *va_list;1 W7 @3 S4 t) m% U" d! ~9 ]
' C4 F" A, ?1 C! j8 }( t" P4 E( D$ x#define va_start(ap, parmN) (ap = ...)0 R W4 Q/ S. D; @8 o+ A* N
#define va_arg(ap, type) (*((type *)(ap))++)
+ @+ i4 n, `1 r* J#define va_end(ap)
( Z+ w: O" ?& L" V) E$ U6 v5 j/ Z& g#define _va_ptr (...)& B5 h8 e& V1 S: @3 H* \# Q/ t
#endif/ ]8 u3 ^$ h9 C: k( ?& U
4 ^# h$ }; T) V4 C" m$ U7 p& N以上为"STDARG.H"的内容.% \, i$ ^" x+ p
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;2 Y0 F: {. ?3 U# L
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
P0 t+ n5 _! n6 LparmN为可变参数的前面一个固定参数.& L3 F9 M: {" S/ q
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 \0 i( ~1 I0 G1 ?. h- N- e$ w0 Ova_end(ap) 结束可变参数获取.
* V7 T, m1 d* M2 P9 |
# u! j3 W! j7 ~$ A4 X8 O3)可变参数的使用实例$ d7 x& s! V. ]2 u
9 U6 `( }0 B2 @! W8 F实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
B- e" N$ `6 J! q% X+ A2 r
* G! e! b7 _! d* p. H, X/ S! v#include<stdio.h>
' z( _; A7 a2 S7 X) k* R#include<conio.h>. K ?6 \1 [$ r9 J/ Y
#include<stdarg.h>( g: T" |' e# b; w# o" J4 c; {
void tVarArg(int num,...);/*num为可变参数的个数*/
5 w+ L* g) w: B- b, W; i! S! l! Dint main(void)- ?( B2 t) f/ x9 ^. C
{
; n- `+ e9 r) k: v/ @& yclrscr();
4 n+ J0 U9 O, f1 f8 Y7 j) h1 xtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
2 v) T( N* @' a+ O# O8 f3 itVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");. Z& l& p: F9 M7 X
getch();
7 W7 \/ b" w; ^0 greturn 0;/ A# Z* W7 N/ Y9 `* F6 [
}
' N4 y8 ]8 ^+ |1 Kvoid tVarArg(int num,...)
' r6 x+ o/ T0 E5 v# o{
^, ]' a# u9 _$ ~+ kva_list argp; /*定义一个指向可变参数的变量*/ M1 g$ M2 Y( \1 g8 J& M" `
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/9 H: {% @2 z; k, l: C/ R) N
while(--num>=0)
% w7 Z- }5 S e u printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,3 Q$ g% k5 T1 E- a
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
* N+ D% R( T: e; B6 O* e$ Q# Zva_end(argp); /*结束可变参数获取*/
6 c# c, Q- o/ q# Treturn ;
$ {- K% j* ~ ?6 {+ P$ E}
% n* [1 f& R9 M' s2 F; G: @+ [3 g" l G& S C
4)可变参数的使用需要注意的问题) P! l9 b& D/ [) o
- q+ U) q# }& x `& i
1.每个函数的可变参数至多有一个.
( v4 E& _. h8 P T& R/ i& I! S2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数. i1 a/ A: [4 {- ~ h+ h6 i/ g+ r7 X
3.可变参数的个数不确定,完全由程序约定.+ \ [# y: L/ L' b3 i7 b
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
) X# E; H& a. ]7 @而printf()中不是实现了识别参数吗?那是因为函数
! x6 Z* j5 b) E8 |" Uprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
2 M' Z% t' ]$ t# k8 P' b的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 8 E& ]( a# d1 `5 |
过在自己的程序里作判断来实现的. . c' P S+ N0 g
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|