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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
9 o1 m" q" x2 [3 t它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
$ p# r( w* m( \: n; k可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不# Y% T1 t1 S/ `& i9 X! }; Y
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有 Z. {& H) m! n, j
实际的名称与之相对应.* ^. e s9 ]! ] M* x' A7 Q
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
/ l3 o; j8 f1 C9 r然而,更多地自由,同样也加大操作上的难度.7 o6 v f& K q- E! |- ]2 @
以下就对可变参数的几个方面作一定的介绍.# u" J: T) j% ]! V# q" G& i
5 u& [. j+ E6 Q* V8 s6 k
1)可变参数的存储形式.
, y/ w- G T3 w6 K* ]7 e4 \6 A" N* ^1 ]7 ?3 @5 P& ^5 q
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,1 E7 q/ ?( }' j+ u% `
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区., a# K& k- P: L! }. ~: A9 E
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
8 C0 i8 K, j, N N# ~% x这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
2 B' A) _" k9 a: M因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
5 Q. @& @9 }; U- D3 W% O栈区:
5 `4 N/ j' M. h3 `. b
9 i- A: b! c! q1 |2 t|栈顶 低地址
/ @9 |5 ]! u4 l0 ?
3 |. p1 ?# v! g- U( M1 m|第一个固定参数var1
" h: T# y% h5 I# N|可变参数前的第一个固定参数var21 ]6 W6 a' r- ]
|可变参数的第一个参数' n0 Z3 R4 X4 d8 G
|...1 H) h6 p4 s' V- a& T
|可变参数的最后一个参数
( t, v3 n% s" ^( g1 ^4 w+ ^4 x|函数的倒数第二个固定参数var3/ P1 ], j% ^- U0 Z+ C
|函数的最后一个固定参数var4* v u& j; I/ _/ {$ i* y3 w
|...
8 r; t7 p) Z( c- L& x3 b( i7 k|函数的返回地址9 v% C7 S( M0 _ L6 L v
|...1 B I1 D& ]7 O# ^, V8 x! S
|栈底 高地址" v" j' @% g& R0 \0 @
0 k5 B' g1 C: F5 e8 U; X) o
2)使用可变参数所用到头文件和相关宏说明
* o( x+ Z% e8 K# n/ K! T9 {2 ]2 m5 B+ ^1 ^7 p( g
在此,以TC2.0编译器为参考对象来说明.
( C3 ^6 O- p) ~. W可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中. L# o& m3 g6 W7 ^3 Z
此文件为:) |4 K) F+ H9 o0 ?2 W% L
/* stdarg.h7 a$ o% x) G. X
6 c7 l6 y% i" s* b
Definitions for ACCESSing parameters in functions that accept" ]- @* V: S) G3 y
a variable number of arguments.
6 }* p! i5 A$ O4 t. y4 c6 {' m3 n$ a( D, ^
Copyright (c) Borland International 1987,1988
+ a) G2 v( D' P( X6 I8 ~All Rights Reserved.. ?" n0 f- `7 p; y9 V( K# d4 \7 @- O
*/
2 T# e; i4 ~9 [3 T; S) Y2 L% T#if __STDC__
; f0 m* u5 z, D, A! v#define _Cdecl9 W) p# X1 _, ^! L0 b
#else4 f2 y& Q: r9 r* G9 f. m; ?
#define _Cdecl cdecl
5 n8 L5 r0 C& a#endif
; i- n6 f* C4 {* v. \
, E8 P: [5 A9 ]0 x$ u#if !defined(__STDARG)
: J& r6 H4 x' H4 }2 q#define __STDARG' z5 a' w& e# `# b3 G8 q! l3 L, L8 T" e
9 H i0 ^. {1 _& ?typedef void *va_list;6 w, m$ m6 k" J0 t) q, K3 m8 u
8 e" w6 g y+ g2 V" q
#define va_start(ap, parmN) (ap = ...)
" L0 i# u8 r6 B7 m. U#define va_arg(ap, type) (*((type *)(ap))++)
& W, S0 d* m$ @# U" O#define va_end(ap)+ n3 b8 \6 n8 q- E8 t
#define _va_ptr (...)# p8 z5 A% B4 H* h
#endif# v9 M# `3 a' f% o1 \+ W$ @
7 w) N% C9 R- @8 z; F3 I以上为"STDARG.H"的内容." I3 y# V* ]4 w
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
6 o! m: O) e, s" P. xva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
% f7 P, O) Z8 |( SparmN为可变参数的前面一个固定参数.
2 |( H9 t7 m& z4 n7 o" |0 fva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
& `) i* I M( o" B+ C4 Y$ e. Ova_end(ap) 结束可变参数获取.
# \/ W; B5 V3 Q( A1 i8 N2 Z& G3 s# T) J* O- v
3)可变参数的使用实例
9 \: r/ y6 W; U$ j6 K2 H! n- v! B8 |4 S$ b
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
5 t+ T% ]6 z% K2 L6 p8 G
" B7 M6 p" ^& V8 x/ }" X#include<stdio.h>
) U5 u- o) @- Y7 i3 b2 o# W! d#include<conio.h>4 F5 e6 X& J# ]$ d$ {0 ]: u: j
#include<stdarg.h>
! l0 C% v% k% N" _, A+ avoid tVarArg(int num,...);/*num为可变参数的个数*/
& \, ~9 Z8 a; J1 @int main(void) F1 z9 G+ x. K, g* z+ S
{
. \4 [' ?3 Z& A* t3 h/ Mclrscr();- ~7 `3 {3 B3 e0 C8 ?
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");( l4 @5 u9 z* w' H' y
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");; v& n( F, Z8 R2 X* H$ l o/ z; g
getch();
! e: x) r2 U* o& Y6 Oreturn 0;# ^( O5 H, H' H& @% u
}4 n' Q1 N# l2 ?
void tVarArg(int num,...)
' r; Z1 b" _; i. W) n{- Q$ F( B# _' f6 U3 u2 V
va_list argp; /*定义一个指向可变参数的变量*/( @+ x- k* A0 b' O1 T( V0 n5 C& u
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
' R& d0 T) o7 u' C7 F- Swhile(--num>=0)
( T4 n T) ~, w6 ]6 f4 f2 @$ B printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,# I0 L3 V( t, e' b
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
4 H3 K m( I5 o0 y4 r& C: Z1 `va_end(argp); /*结束可变参数获取*/
/ B/ _' T# [0 Ureturn ;
y! y$ n" h- Z* m* q) j$ e}6 T- q$ w2 D' J) H" E! B8 h
2 t8 y8 z1 t( v7 f' P. s
4)可变参数的使用需要注意的问题
- W* q9 V' l Q( G/ I
% j# A* g- b7 t6 e; {1.每个函数的可变参数至多有一个.
" {) m* J B4 p# Q2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.- z5 E, E! G, f" _* n: l3 l
3.可变参数的个数不确定,完全由程序约定.
+ k% z' Q7 m" r! f2 j8 |3 f) e( l' z! m4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.! K( t/ g* t* ~- C
而printf()中不是实现了识别参数吗?那是因为函数
7 b4 w8 J" Z& a& W# a6 U; zprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 6 e& Y" g. S9 y' \/ c
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
8 v; k- C0 H8 L0 e$ w过在自己的程序里作判断来实现的.
2 H* j% q3 X) z7 r0 p9 ~+ d. M% O5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|