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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.+ D; y4 f; K, U0 Q
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
1 U5 ^* Q$ p. X' `1 J' Q( c可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不6 S5 Z6 N7 d) i, k; y& S
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
0 m, S9 `0 [( K实际的名称与之相对应.4 }. Q) s7 q& W L% [
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
7 `& ?9 @! ~6 {. |然而,更多地自由,同样也加大操作上的难度.& m+ [, Q6 V, I$ T4 B" M) r
以下就对可变参数的几个方面作一定的介绍., V/ y% V! P3 z, \
; x5 x/ j# s' b& s4 F1)可变参数的存储形式.
2 V$ p5 k3 r2 ~+ M# `9 e6 j# M2 Q$ m8 b3 O! P" [
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
3 s* K- j/ |2 K+ B存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
0 h/ {) U& ]6 Y3 }% X在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,, u: M) j% x( L- n ^0 Q5 z
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
! Q+ g" `2 x+ c' X+ R; a- I! s因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):3 R. g) Q/ Z. |# n/ O# r4 o
栈区:) { V" x* J; A
7 M- |5 B1 g( ^" E
|栈顶 低地址) q' G* m- m$ \9 m( d' O; W+ y
2 l% `8 m+ a" w' P# @$ D|第一个固定参数var18 w2 Y2 U, V* k& q+ G
|可变参数前的第一个固定参数var2
. u. M& J- t9 l v* O0 T|可变参数的第一个参数9 R c% ^) _' i# f6 v% p
|...
) F- J# L Z z# A# Y, B# Z7 V# \- N|可变参数的最后一个参数
9 H5 S; {! C7 ]7 C& P& O|函数的倒数第二个固定参数var3+ w- A$ U+ s! m! P! |
|函数的最后一个固定参数var4) z x# |0 Z# y- ~/ w# B2 Q& S' T
|...
4 X; g2 Z! f% }|函数的返回地址3 m1 r6 o4 f: L+ z5 n
|...
6 Z' T6 j' y2 n9 j. s* N|栈底 高地址( P' o/ e" m* A3 f Y6 p
- Y3 B$ h" R% m# A% J2)使用可变参数所用到头文件和相关宏说明/ t1 k/ ~' a" j! J) q$ B
7 z0 g3 G+ N. s, ]7 m
在此,以TC2.0编译器为参考对象来说明.7 x& d/ _5 r! [
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.! r, D! ^; o7 Z( s9 j/ M2 Z g4 u
此文件为:1 e8 n$ N8 y- c
/* stdarg.h
) U2 }8 N$ X' ~" V! h* @ H
2 ~ E( c' M- l' d( \( O' P2 mDefinitions for ACCESSing parameters in functions that accept
6 U# W# X9 }$ |$ }8 Aa variable number of arguments.
" T! E8 ^/ E+ x! r# U6 E' F( f$ b8 F0 S5 m5 Z" f5 z3 f8 k) q
Copyright (c) Borland International 1987,1988
% j/ g2 j9 K9 Z$ a2 @9 { ` EAll Rights Reserved.
/ T0 j% q' `7 G' e0 P+ \- l*/
+ k$ u, Z1 ?1 u, T/ y) w#if __STDC__
( N5 m% S. G# O& {#define _Cdecl( y, S$ G% t5 Z. u
#else
/ A: X: p+ {( U' n9 K4 q+ S#define _Cdecl cdecl8 Z& u2 V5 H6 Y1 z5 W$ b& `
#endif
5 P6 h1 |, y& p8 P) K, [6 ^9 [! i) z: Z' q# _' [
#if !defined(__STDARG)
: @- F3 ~6 b! t4 S' p- c3 {7 f#define __STDARG
- [5 a5 N: Z: ?' J6 p. m6 C3 N
3 o* I9 I/ H! h4 Stypedef void *va_list;6 z: s/ L' Q1 F' i/ ?
# `; Q4 e/ ]8 O; q
#define va_start(ap, parmN) (ap = ...)
* w4 k$ M0 L& d! ^) R1 x#define va_arg(ap, type) (*((type *)(ap))++)
v' j2 v5 [3 d% |& ~! h3 H#define va_end(ap) q+ i) c% ~9 j) W
#define _va_ptr (...)! t1 [& O# W( F+ B- L6 J+ B: D
#endif+ |' j+ d2 R" f/ Q5 s0 h& q: ?
! P% J9 T$ `; Y6 p7 |
以上为"STDARG.H"的内容.8 z* i% ^ N2 J' S7 a2 h) ]
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
; G2 E x' G" J8 a8 k/ V f7 K$ }; ava_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,, j5 }, Q/ c- {, h( c
parmN为可变参数的前面一个固定参数.; J: M% R, y# \: {% o# p7 h
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型." S: t7 w( s6 C2 _( \1 ?: Y
va_end(ap) 结束可变参数获取.( i3 U; t( I* B2 y# T6 r
0 Z8 r( X) b5 l- [$ r' W3)可变参数的使用实例
( p( l K6 L; F( I. S: z/ P! B1 `* g# o! o' O
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串., a q9 A8 F3 A0 S& F9 q
% T0 `5 t/ N; W#include<stdio.h>
7 Y1 A$ ]/ ~5 B. x" B T. X#include<conio.h>; F0 h5 t" Y2 \9 y0 j
#include<stdarg.h>
2 \, u- Q6 P6 f) E: R4 evoid tVarArg(int num,...);/*num为可变参数的个数*/( k. D- J* |) d2 t
int main(void)
! t+ T; z" P( h{2 w- V. r2 @+ J0 ~/ q
clrscr();
* R& @. F! a6 x2 u9 y, `% PtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");3 @' `# i; C, B
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");! {5 O/ H1 | |3 a
getch();
& D& d+ D) I7 Z1 _, c$ R9 P" [return 0;& M; ?: b7 w2 N$ i
}7 L4 {1 W; O+ q4 W/ H$ t( ]% x
void tVarArg(int num,...)
4 Y) S/ J T2 S{+ A5 g" M; B1 B0 o* }) ~$ c
va_list argp; /*定义一个指向可变参数的变量*/
* K' ?4 G( f$ qva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/0 Q$ z$ A, }8 q9 e
while(--num>=0)) _0 d& s- Z( e) ^7 w
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
' L. d1 b. Y: A# A" Q4 x5 `( o/ E. q 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*// z! i0 I" l7 r6 v* x* H" U& m
va_end(argp); /*结束可变参数获取*/
& O- t7 Q7 f0 j- \& I$ Ireturn ;
: e, O- ]- M' A) F9 V/ }}* C3 {; _% ?" }4 I: f7 |8 K: K
6 \& \5 @; |4 N+ p4 b+ t5 a/ Z
4)可变参数的使用需要注意的问题
! j. R& v( I( S' T8 f3 k7 w% ~
3 @8 u/ c8 r$ ^1.每个函数的可变参数至多有一个.- Y2 V/ `/ P! l# b/ @+ Q/ _! y3 f. p% c
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.+ b* E. W& {- w) W
3.可变参数的个数不确定,完全由程序约定.# x! C* `' e3 l
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
- G% N6 n; Q8 ^& r4 P而printf()中不是实现了识别参数吗?那是因为函数
1 i3 y4 B- H& `8 A5 kprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
: I/ o$ w, n4 I& P! Y" h, Z! j; ^的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
( S5 @7 N- i0 L T3 {过在自己的程序里作判断来实现的.
% n+ b- P9 _2 S+ D! m; H5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|