标题:
函数的可变参数详谈
[打印本页]
作者:
zw2004
时间:
2008-1-21 19:42
标题:
函数的可变参数详谈
可变参数的英文表示为:variable argument.
% K$ K/ X: ]+ G
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
+ ~" b$ D" C, e7 }" ]+ K
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
2 @) p. P# R% m8 b1 f
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
- G# C+ u: n% L- @8 H% A! F& d2 [' w
实际的名称与之相对应.
& X) U+ {: I8 x# r1 l, j# N
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
' l( y, D1 c; y/ |; k( i+ }: }
然而,更多地自由,同样也加大操作上的难度.
; s+ x ^4 ]' \& u1 X
以下就对可变参数的几个方面作一定的介绍.
( H2 ^0 X* W, l8 P# f* o7 |3 z1 q
6 D; s0 g' U& f* Z; t
1)可变参数的存储形式.
# k: r- q8 {# _3 i( J d5 _
0 c% C; x: X# ?
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
/ s7 Q2 I: b" J! N( n; U$ U
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
$ k I1 A# W# J7 t/ s! d
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
& }5 s- c2 S% |8 h. A
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
3 Z8 x% u! K2 N
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
" G9 M' \/ T! w+ `9 p7 c; r
栈区:
& M7 r. E% {4 W% d+ j. R
/ @4 T! ?- P7 T- {. D: P5 s! r
|栈顶 低地址
6 |; o5 l1 C: c/ z
Q; m9 z0 b1 X
|第一个固定参数var1
" ^0 ~* ^$ I. Q# q# t
|可变参数前的第一个固定参数var2
0 B' g( e4 N/ D4 X
|可变参数的第一个参数
1 @1 u1 `9 L. c0 ^3 m3 G
|...
" P) r, m' R5 R! s6 C) r
|可变参数的最后一个参数
' c `+ @7 O1 k
|函数的倒数第二个固定参数var3
, Y* W. v- s% O+ C
|函数的最后一个固定参数var4
9 g# e; |$ G8 ]' f8 F* N
|...
% n0 N: n3 K3 R" t) [1 c
|函数的返回地址
# M, |1 Z; `, t
|...
/ R7 `. z4 N2 N1 d/ c6 x p
|栈底 高地址
6 D$ i# o# D1 _3 Q4 R
8 o( `2 A' S; i/ j; }$ z0 c: }
2)使用可变参数所用到头文件和相关宏说明
9 K; Y8 W! B+ m8 L
2 D; t4 I/ @2 y( e( M- V
在此,以TC2.0编译器为参考对象来说明.
2 a8 |6 u2 I9 q+ @8 B
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
& ^& v) k0 E: s* D8 I/ ~: w
此文件为:
% A# c3 M4 j" X" Y4 ^) ^
/* stdarg.h
3 }9 w* K3 K# y, W, x& Z: J7 x2 O
, L! Z: T- k) A
Definitions for ACCESSing parameters in functions that accept
0 e7 `$ `8 W* H' }$ s4 E$ ]. d" [
a variable number of arguments.
5 r( k! ]+ `4 Y
& g- G2 O& c0 r3 T8 [2 x
Copyright (c) Borland International 1987,1988
. c, a. E) o/ m, R
All Rights Reserved.
( F$ c j, }8 Z) l% X$ {( q9 f
*/
. o/ r! k0 L0 H: A
#if __STDC__
$ [# g C7 A8 m1 @' V( q
#define _Cdecl
' Q% |- U( C3 v8 I0 l$ v
#else
+ h! z1 u! m! N+ F
#define _Cdecl cdecl
: Q. e- S; y4 ~+ Y8 ?- V
#endif
3 ?8 K' P, M* @3 m" c; w# L
; K. M; p. A, z o
#if !defined(__STDARG)
& w. h0 _" ^/ p- R! g2 l* L
#define __STDARG
+ x( d8 H M6 ^3 O: t& p0 N
* C. Z. X/ [. m' V- S, w5 g
typedef void *va_list;
C4 r# {0 I; S! h j5 @
" ^8 k0 _. L3 S, L1 a2 r! ~
#define va_start(ap, parmN) (ap = ...)
) y! P+ ~- E4 E3 Z' s P$ O
#define va_arg(ap, type) (*((type *)(ap))++)
, g; v# R. |. g8 q+ H% L2 t+ P
#define va_end(ap)
3 t* E2 l" o1 m: V
#define _va_ptr (...)
; V+ I/ O+ d+ c' c$ \# v5 t
#endif
! T) B! y& l; C6 F& N. v
. d& K B! }8 ~( W. ~$ n* n( R
以上为"STDARG.H"的内容.
1 d1 }/ t5 ~1 R% n& v
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
% R E+ ?7 T- f1 q1 B1 B" s; L" X
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
' D- n/ n- N5 H7 n$ }* \
parmN为可变参数的前面一个固定参数.
# @% X9 S5 R' ]: h
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
9 M6 @( F. [7 ?% E
va_end(ap) 结束可变参数获取.
8 \8 O& s* i6 Z2 E* p3 Q
- d" X" h/ p8 ^+ g6 V1 M) e2 q" K' h
3)可变参数的使用实例
0 R; I5 u, s' ?$ J1 n
/ r* O) y+ r- }2 j
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
8 R5 k$ C+ y9 x, }2 L; h! h1 x3 F
* S) D+ X, ?) v% g5 ~ @- C
#include<stdio.h>
% K2 \' O% z/ E$ K
#include<conio.h>
, Z, `6 s) Y. H
#include<stdarg.h>
1 [! n! r7 }" L% Q+ F
void tVarArg(int num,...);/*num为可变参数的个数*/
: D9 N( e+ z3 ?/ g. U% y4 a
int main(void)
+ c& Q6 D2 p; {9 w6 [! C) a
{
; s+ h. C( |5 B* z$ i2 {
clrscr();
4 Y' V1 F2 P8 d% _
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
9 Z: w2 L8 a4 a
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
6 \4 L8 G, A8 k7 s
getch();
$ X( k9 n+ b l& v5 I' o
return 0;
N0 V: d2 B, d
}
2 z4 ~, B- D% b
void tVarArg(int num,...)
! P! p7 S) K+ R) z4 G
{
: x$ v% O' A6 a* D& \ o
va_list argp; /*定义一个指向可变参数的变量*/
$ n4 k) t* I# u2 u2 t- H, Q
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
& p) B7 R8 d4 U$ s. q. }) ^
while(--num>=0)
5 P/ L- C/ t$ C6 T8 L9 ^
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
+ R, A8 o: c# {8 ?- @. S
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
/ S+ I# _6 r( Q+ K
va_end(argp); /*结束可变参数获取*/
/ U" D- }4 g. u x2 w
return ;
9 [8 R2 f# c4 z
}
3 S B* D: X }: A$ Y) X
, @( G5 }2 v u0 H0 ^/ w
4)可变参数的使用需要注意的问题
3 \& D2 K# t# w4 p4 s& |5 W
9 S* n1 X3 h7 F
1.每个函数的可变参数至多有一个.
9 |! W z5 o7 G5 P( ~' n# w. M
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
: s s& g9 Z( v9 V' e
3.可变参数的个数不确定,完全由程序约定.
1 W4 w* w/ h) M6 i
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
* l+ C! s, U4 P) }
而printf()中不是实现了识别参数吗?那是因为函数
l! j. k6 O) n. o1 u% C+ Z
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
$ o8 N9 \' a3 ]; E) Y! H4 i4 C% D
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
6 V. D2 d7 W8 F5 _4 O
过在自己的程序里作判断来实现的.
; X7 U2 S" v2 y7 ?( u0 Y1 f
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.
欢迎光临 捌玖网络工作室 (http://89w.org/)
Powered by Discuz! 7.2