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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.* Q* |% n4 ?! Q$ ~" F* T S4 M2 K: \
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
* L& r3 ?2 ]' `/ m可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不& F. _3 V( I$ [; g* x" h
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
! }% t6 o( W8 x& t3 v# l实际的名称与之相对应.
- T& f. S, r- ~7 I. T @% S由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.$ l) J7 B8 J1 H; { i
然而,更多地自由,同样也加大操作上的难度.
* ^9 U! `1 F. y) w' F以下就对可变参数的几个方面作一定的介绍.
$ v" {; g7 @+ Y( a! B- R V2 Q: u9 O
1)可变参数的存储形式.9 h2 t/ {- ? K$ y; a9 C5 G- |
) r4 S }( b) s: ~
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,( q, F5 u& G: U
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.* o4 K6 b. x, d, Z7 ]
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,% T0 M& t6 I: {
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.' z/ _3 S8 L7 P+ t, S
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):) O1 Y0 ?: W! D/ C) v
栈区:5 Q: C# h" B# k. A% S
6 ?2 b) l/ l) k' y5 o" N! H5 r4 S|栈顶 低地址
: H* m& X. k% e1 K+ l& A: R* w
8 M7 P( S; a! G# ]: b|第一个固定参数var1; e7 ]- G' @/ G
|可变参数前的第一个固定参数var2. a) H( P9 k% O# n$ m3 u
|可变参数的第一个参数
& I4 n1 O8 b- t, I, Y5 N( `7 @( X|...
& p6 c2 |& a8 d8 G0 Z/ M0 l|可变参数的最后一个参数+ f( q5 \* `& j+ |5 n
|函数的倒数第二个固定参数var3, M( q9 w0 z% F( D
|函数的最后一个固定参数var4
+ g5 T, g. Y! D7 w7 H|...
# _" {7 g) F1 L; ~/ i|函数的返回地址
% T; V: x1 @0 L|...9 I9 {3 O; H9 l% o$ q m1 r
|栈底 高地址
: p. ?/ j0 {( S7 [/ ?( H% W3 Q" q5 W/ R! W
2)使用可变参数所用到头文件和相关宏说明! J! V4 ^2 v5 @5 s0 C3 a3 @
& g9 s v, B+ f' _& j) e4 J在此,以TC2.0编译器为参考对象来说明.% q4 q6 z7 }% f( g% a' N2 P l
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
! ]" M! X" M9 B& H5 w* E# Y此文件为:
x* f( m1 ^/ z3 ?# F' g) @5 |5 Q/* stdarg.h
# E8 v( j6 q4 Q# R+ A( `
1 Z" y9 _) c. H/ pDefinitions for ACCESSing parameters in functions that accept$ G, m( y/ E/ j- I
a variable number of arguments.
3 O1 \' h1 P d+ E$ I$ T( `5 p0 r2 Q9 B/ X- b
Copyright (c) Borland International 1987,1988/ u& N' L$ |, s; _
All Rights Reserved./ [7 m4 F, Y+ ?! A
*/
+ |) U$ z/ j$ L9 v#if __STDC__$ p( G+ X7 e3 S
#define _Cdecl
7 _3 _4 O- F. D5 F$ B8 r' v% d#else0 G7 ?$ {5 O( _0 f& ^# V! |
#define _Cdecl cdecl4 j$ W# Y8 j7 ^4 T
#endif
) b2 [4 L5 q4 g( L/ i5 M2 c
" ?4 `* g$ y3 ^0 o4 s#if !defined(__STDARG)) i+ m5 L- `/ {# C0 G% }
#define __STDARG, J. k, l' X* U
' `8 I/ @7 [5 d+ R( wtypedef void *va_list;8 ?( ^6 d3 G0 G% Z
# R2 m9 ~+ ?# h#define va_start(ap, parmN) (ap = ...)# R; @7 t! v! ]8 v' P
#define va_arg(ap, type) (*((type *)(ap))++)$ f4 T) [) b9 \( j
#define va_end(ap)/ N0 {# B! T* \( }% n5 [* I1 @
#define _va_ptr (...)
% V2 `+ y s( Z$ L5 q" q#endif k0 E6 |; Q) t6 y1 A! s
- M8 o5 a$ J7 O( N
以上为"STDARG.H"的内容./ G+ D5 t) ?3 [9 Z5 A
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;4 a$ Z2 p5 ^; @! Q& [
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,% Q; y+ R+ |2 a# W
parmN为可变参数的前面一个固定参数.
0 D0 R# x/ X+ X8 E8 bva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
' ~) _$ M; \6 G% D) e- mva_end(ap) 结束可变参数获取.
- _% x" M# d9 |/ t$ ^ R m5 a- k1 T& g
3)可变参数的使用实例) v- L# Y" |2 c- J3 s- E
# H8 r$ G, o7 V0 x' O实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.8 s& Z8 e. X) |
( L; U D3 c& [/ v# V$ [1 L, ? N6 f#include<stdio.h>
0 k8 i7 v4 j. z; p- J#include<conio.h>1 z# y+ _* K6 _. Y5 E7 u
#include<stdarg.h>( @5 E4 f! Y! f+ c
void tVarArg(int num,...);/*num为可变参数的个数*/3 P( s4 ?- I6 G/ s- V) y3 Z2 ^
int main(void)- O1 O2 z7 @! _. l0 b
{$ H9 u7 R+ B8 T5 S! t$ D
clrscr();$ s+ f/ {+ ^& J: I) `
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" p* c0 J+ h: t+ m
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");) d% c7 D8 [$ s/ D( |' ^
getch();" g& Q! q- G6 x4 t) v
return 0;5 @( d7 V) s6 ^9 s
}* o n4 J4 a' Z* H( A' \# R ~
void tVarArg(int num,...)0 a! B, Y3 @6 u% B% r! Q
{+ T8 I% J( l* L8 }! s
va_list argp; /*定义一个指向可变参数的变量*/' B6 q# W5 L2 K6 Q, s3 C! ` O- e {/ x
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
- l0 O. `2 J! m" j& Kwhile(--num>=0)
8 Z- y! } A! q* z1 U: T printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,4 u1 Q5 a* n! s- _. }
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
! e& [' H2 q: G' `# pva_end(argp); /*结束可变参数获取*/
" e/ Y2 T" x$ e. G, Z: Breturn ;
" ?0 B( \1 `. U+ }4 a* o4 M$ I}
/ |4 U" s# }$ F
5 S, A4 V9 X: R4 a; p5 p9 R9 d4)可变参数的使用需要注意的问题$ O, f, X4 `8 W: ^/ n
: e6 p0 w5 ^7 p7 K# ]. s
1.每个函数的可变参数至多有一个." ^- N K; y" d
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
- j: }0 z1 N0 F$ c1 o1 n6 d: w3.可变参数的个数不确定,完全由程序约定.) @. F6 Y& |# n" K/ [
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
7 Z( m: D% z% J( m, E/ r2 ?1 k而printf()中不是实现了识别参数吗?那是因为函数
; X% O2 v; m# w. [# m: w; Gprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg * x, u7 N+ v" f9 C6 a( i7 u* w2 h
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
Q% v2 T7 E+ e& Z+ e, l过在自己的程序里作判断来实现的.
& U" f, \# L& V: Q [+ K; s; H5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|