标题:
函数的可变参数详谈
[打印本页]
作者:
zw2004
时间:
2008-1-21 19:42
标题:
函数的可变参数详谈
可变参数的英文表示为:variable argument.
2 M6 I$ G( ]4 r3 r+ n. Z
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
/ {" n; R) \0 m1 F
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
9 E+ d( Y# {' G( N
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
) ?- b/ o* V6 q0 ^7 E: U9 ^+ O
实际的名称与之相对应.
" k$ W+ l4 n$ o1 b
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
, E+ `- F! a0 A. g, g& N# s
然而,更多地自由,同样也加大操作上的难度.
- x& y7 g) W+ c* F' ^- a0 k' H
以下就对可变参数的几个方面作一定的介绍.
6 N( N6 m6 R$ {# p5 B
6 w7 s4 ~/ t- j% X9 [0 _
1)可变参数的存储形式.
: _9 i" p) [. ]. h: T8 @" H! G
3 s" o& |8 A+ ^: E2 P
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
( H5 D9 r6 x Q# Q
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
/ C4 l/ @+ H8 K. b$ h4 a1 v& o
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
0 b, S# n4 L3 d. H& j4 E! X
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
+ r& M7 M4 q. l+ Z3 _/ X" j6 ?
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
; K9 e2 s% O! O
栈区:
; l, x: ?/ S+ V3 [- E
9 k9 h& R: v( l9 n' c& w5 _% n5 D
|栈顶 低地址
5 k1 C2 d f4 N
& m5 R: ]& T# ?# J: Y: f! |' ^' _
|第一个固定参数var1
" y% Z: a7 k5 H1 }9 u
|可变参数前的第一个固定参数var2
u. {+ K0 F+ Q+ a8 U, S1 [
|可变参数的第一个参数
' H% }/ e" f* f9 C# }! p
|...
: E% s' q. _3 j: ^+ O. {1 X
|可变参数的最后一个参数
- f1 S+ I$ ~& B' v( y
|函数的倒数第二个固定参数var3
! x1 \1 r- G! U! W, `" E
|函数的最后一个固定参数var4
: h2 N9 k; K: Q: y
|...
: x9 O" b' F! s' D; e+ }
|函数的返回地址
3 n9 W$ X3 d) R
|...
& l% k) j3 N, q
|栈底 高地址
, r9 {0 j ]( K# [* v
5 U" x9 U" Q# m6 M3 G$ Y
2)使用可变参数所用到头文件和相关宏说明
' k, H5 i* D" {$ t
% _' j2 a! y% h+ N
在此,以TC2.0编译器为参考对象来说明.
4 \3 i) C. n) L+ z) `9 P& t, _" T
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
3 a X, q* S' s; a
此文件为:
8 H, S/ F, r/ N. ^
/* stdarg.h
: S8 i/ N# M% l2 F5 L8 k$ W% @* k1 Z
3 Q) r* t5 `7 j3 P+ t: u4 H
Definitions for ACCESSing parameters in functions that accept
) r( g1 ]/ c7 k; e; }, y
a variable number of arguments.
+ Y, O! K t) _- i, G
# B4 `2 G% q$ Q2 n! S) \+ v
Copyright (c) Borland International 1987,1988
0 U0 ^8 i, M6 f X8 ]" }% `
All Rights Reserved.
" M4 c5 ~7 g5 I% h w5 w- s' r
*/
& @' y/ Z. p/ `" U
#if __STDC__
. e8 S+ b) {$ C; W Y5 m; l
#define _Cdecl
" e: [2 J, y9 |# o0 E2 z
#else
|( k% j6 F# D( Q+ l/ o* j% H% y! n
#define _Cdecl cdecl
/ v( i2 H G7 W
#endif
# @! w- `0 d7 ~. c d1 G
, ?# X% D$ F8 G: ?! a! q; K. }
#if !defined(__STDARG)
( W# J L% R8 t, s
#define __STDARG
/ N$ W6 v; v6 T0 b
, o4 q8 |0 `) Q
typedef void *va_list;
/ Q; c( V0 t* _/ r' f
$ U& E# s1 E6 j9 x7 _
#define va_start(ap, parmN) (ap = ...)
6 p7 t( W6 z+ V! J# s% [
#define va_arg(ap, type) (*((type *)(ap))++)
6 k6 V x3 s& u$ _: O
#define va_end(ap)
4 Z( w5 ~% d' z
#define _va_ptr (...)
V; r* R( f# ~. M5 v% H. Q9 ~% J
#endif
1 l# c" t f# M! m) v
* K5 s* ^8 n+ b3 y1 I
以上为"STDARG.H"的内容.
$ d) @- [: H8 P5 X5 b# r
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
# L4 ^9 y# f7 s# ~ [
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
e: V7 S7 l8 D6 x0 ^
parmN为可变参数的前面一个固定参数.
6 o2 ?3 M4 s; l7 g
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
+ Q- E1 G7 ] G/ {2 m
va_end(ap) 结束可变参数获取.
5 k$ L+ y' {. `, |0 g. V H& a
3 S F; S& N5 t( [8 |
3)可变参数的使用实例
# h. w c" ?. d {& w
, r) ^$ o, n2 _7 L( r4 O' g
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
8 h$ R3 l6 [& g+ ~
$ A2 x. M/ K2 B9 u9 ~
#include<stdio.h>
/ _9 f) q' o6 o) @" j% t8 ~6 P% W
#include<conio.h>
7 K$ C; A$ h- z/ R" S4 m3 |' h
#include<stdarg.h>
7 A) E) B4 l+ [& P5 G3 h6 W
void tVarArg(int num,...);/*num为可变参数的个数*/
5 L4 H, I) h9 b {% d4 Z0 @
int main(void)
# r2 J1 M/ b ?& R1 {
{
0 Q& A9 r! v: d" g+ t
clrscr();
2 a0 ?/ ]3 s! V" U/ x6 J# }* @
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
4 @6 m( m# T2 [6 K/ A) \; K7 e
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
: O' [0 V# F+ I9 q
getch();
9 k( t' T6 V/ |& {" ~; e! g" P* V
return 0;
8 C7 j t: L# O
}
( r4 a0 M- U+ W* b
void tVarArg(int num,...)
2 _3 h" c0 t& c* y/ G
{
) @7 M" }( u- D* N' a% G
va_list argp; /*定义一个指向可变参数的变量*/
+ h4 k$ g0 x0 o$ E
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
: j& X' X2 i! M/ d! C
while(--num>=0)
& G/ w' }/ ]3 G% K
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
% y5 m5 p6 k' \
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
l. I- i7 W- J% E! ]0 A
va_end(argp); /*结束可变参数获取*/
7 j+ V% a. ^4 p% S3 c, G! q' v
return ;
9 W0 ^4 t# r, L8 A0 B( @2 T
}
" D' v6 X8 X' W2 m
- _5 g* C' u- N
4)可变参数的使用需要注意的问题
" [7 S( M* O# u+ p$ K$ t$ F8 _1 ^
1 ^/ A; K' h+ F, k* X; y
1.每个函数的可变参数至多有一个.
4 l9 p- O w' ^- d0 _0 [2 t
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
& x' [3 T/ V! j$ q3 j
3.可变参数的个数不确定,完全由程序约定.
3 v- z; F, J$ q) ]- X2 t; P2 ]
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
2 y# y- ^7 H9 E0 R+ V I1 Z
而printf()中不是实现了识别参数吗?那是因为函数
( [ P1 y! ~8 X
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
/ s! C4 a* c& N4 W
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
1 G8 h# Z" B% y. K j
过在自己的程序里作判断来实现的.
- @5 ]; o1 h3 l. v; V U$ l
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.
欢迎光临 捌玖网络工作室 (http://www.89w.org/)
Powered by Discuz! 7.2