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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
- `* l9 e5 B# V它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
1 _6 P* \6 g- }可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
5 T h f1 f" U- G4 U; r Z( y定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有) D' m' H8 u+ X0 a o
实际的名称与之相对应.+ w% K& B: T9 _. V. c& k
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
5 h1 `$ L# |% Q8 ?: w" ^然而,更多地自由,同样也加大操作上的难度.
/ f6 | W5 y. t: ]. ^2 {0 l" V以下就对可变参数的几个方面作一定的介绍.' W$ r7 w) b, m9 s0 a" U# F0 M
9 ^% g$ G9 @+ P5 h, W! E
1)可变参数的存储形式.
; R& C! f: f: ^5 ^1 Z) w
2 o: P+ Q9 u: J3 C" b) i! a/ k6 D大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,& [2 T% Y7 w/ `8 @
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.5 { V# d3 W% @* B
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,8 F. k0 v$ K) c9 x: c% B8 r3 ^
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.% _6 o/ E2 L; b* `) L
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
7 j1 M# D1 c& j" `5 A, z栈区:' l# G, K. u, f- {- X$ u4 u
4 k& E6 o% T/ z
|栈顶 低地址8 o% x0 |( U. N( u
5 {+ e. d+ k2 q+ }- y5 f# U1 l|第一个固定参数var1
5 @2 [8 Q. }2 \! y/ r|可变参数前的第一个固定参数var2
7 S0 i' T# \% _* F! t p3 A|可变参数的第一个参数/ ^) ^% m, M* s9 M. _) x
|...8 ?+ K( J! d0 J' ~+ E8 E
|可变参数的最后一个参数
9 G( o7 y; U7 k|函数的倒数第二个固定参数var3
9 _! `0 Z* Q$ v8 c+ i8 m|函数的最后一个固定参数var4! \' L$ E7 F$ @- u4 e P$ I9 _
|...
6 A' ]7 j7 w- Y! {% S0 @! V|函数的返回地址
# o$ v! k9 G$ Y- q|...
$ l7 ]! W i( z- a: a|栈底 高地址7 z. t' C9 {9 X8 G
2 _) b0 u: P6 q; B2 _2)使用可变参数所用到头文件和相关宏说明" m" I* P' X6 a0 x" s
% M$ N$ G) l" R8 W& T
在此,以TC2.0编译器为参考对象来说明.0 j9 @; @2 @! T# m
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
# D) w2 v: ~: S此文件为:
, z- B0 Y# K R/* stdarg.h
% o3 q; z' Y5 z1 \" n
- I: _$ a. l* \. a$ `Definitions for ACCESSing parameters in functions that accept* x* L6 `( m% g2 }# n/ P) F
a variable number of arguments.7 t5 b+ ?4 C+ ~' v: G
$ B/ o+ B8 m4 ]* F
Copyright (c) Borland International 1987,1988; r: n) n ]( i( t
All Rights Reserved.! u- B1 o! ]: z; ?: n+ F
*/5 T" s" U7 J6 e j, f) N
#if __STDC__
5 p, }/ C7 X: u% x1 p+ J#define _Cdecl4 p& @% N& a9 v& B/ _0 p% f
#else
0 G9 z, O5 d4 a: _#define _Cdecl cdecl
! w6 \0 U+ \- w9 K [#endif
/ e" Y) K+ ]' v. |; m$ K9 b' O6 c9 ^# `
#if !defined(__STDARG)
* w2 @: _/ |2 @5 q#define __STDARG
3 f2 k1 Y# M3 V) _0 K1 g4 N1 u& Z: y6 O, N5 R8 X
typedef void *va_list;
8 t9 j2 o; t4 ^
: Z( S# |5 `4 b3 l, v8 I! i! t#define va_start(ap, parmN) (ap = ...)8 j4 Y" o1 V! e6 u
#define va_arg(ap, type) (*((type *)(ap))++)
D; W5 s: f& m: P) O# V#define va_end(ap)# L- s4 e8 W+ h: V
#define _va_ptr (...)
% F9 p6 o T, h y% L% A+ c' i#endif
+ ?, W$ U7 J# F% {& A/ \" |1 k" c8 a! t
以上为"STDARG.H"的内容. {7 P1 Y' N% I! n0 Y
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;! G" h7 {; @; |- n, u9 W+ C U
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,9 N+ h# O/ _1 e1 }. n
parmN为可变参数的前面一个固定参数.2 n9 H4 b2 k$ D+ N5 r
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型." L8 j2 T* f6 z
va_end(ap) 结束可变参数获取.8 e B5 b" _( l% |! Y7 ?! \& Z$ i
: v; V, }7 ?1 {) ]3)可变参数的使用实例
, C9 y7 J) q" [6 P% b1 T$ ^: r7 Q& b) T: K: l8 x
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.3 G t5 I+ R$ y, N x
. t( S4 K" P% f; L/ s$ h#include<stdio.h># D0 `- v" z# S3 W6 G) s% J
#include<conio.h>
9 ^: L8 v: F2 G# Y9 v7 G k#include<stdarg.h>7 b9 e( l# }' L- S- O) g
void tVarArg(int num,...);/*num为可变参数的个数*/
1 c- ~. G/ M2 C- W6 R# o% D9 Bint main(void)
! o: E; c2 [+ {& t9 F# H{4 a3 Z& R1 a0 @
clrscr();
! f8 ~, K4 O i, U7 XtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");6 \4 K# v- ?3 B4 i
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");, G. y) B7 {2 L3 S$ y
getch();5 K0 u2 B8 D6 W
return 0;( q% P E2 Q& \+ o
}$ \" j7 I$ C2 n! k. C( s
void tVarArg(int num,...)* R4 T. ?0 F# R# @
{& C! W% k% F" y F8 F
va_list argp; /*定义一个指向可变参数的变量*/9 D+ N6 @% i- x' g' Q8 w
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
( v; k6 O. b' [while(--num>=0)
& X. n9 T" N6 _ P printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,' S. \3 L5 e) c% p
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
1 Z" x9 N) u% ?# d- i$ j4 M% n1 Tva_end(argp); /*结束可变参数获取*/" N% G- M# c" J& Q* b( g
return ;1 Z1 e' M) ~0 {6 k
}
! A# F( a, S Y# Q. d) s6 p5 P2 ?
4)可变参数的使用需要注意的问题
# s1 P' F0 |* m+ g7 v: B* a& q8 x; _! m) v. s9 X
1.每个函数的可变参数至多有一个." {( f' Q9 s3 X/ S6 V& k$ c
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
0 E% |, u# ^8 [4 V9 b% o1 I3.可变参数的个数不确定,完全由程序约定.
7 G8 O' T) s& ]# l# p0 \" P4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
0 u/ @) i3 e" L, [$ P4 }0 j而printf()中不是实现了识别参数吗?那是因为函数 ( ]9 N% k/ m9 b, h8 H
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
2 i) ^6 ~) W0 u的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
7 h. [9 E" R' m7 ~2 l. k过在自己的程序里作判断来实现的.
7 z+ H1 }. G O1 c9 G6 P1 |5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|