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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
, v1 W8 C- f1 ?, q3 y它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.# ]3 \2 ]( d; X" C; N$ p1 l8 s
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不. z. C3 c6 Y* D; W/ ~
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
4 F" H8 p2 S: \! j+ R1 n实际的名称与之相对应.
3 C% F" f2 }3 z! j9 D2 i5 _由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.4 @9 c$ x/ S* b0 V
然而,更多地自由,同样也加大操作上的难度.5 I% F5 U/ ], \; V& R7 y
以下就对可变参数的几个方面作一定的介绍.' U. t8 i% {% E- p( W7 o
4 h$ D, H1 X. z7 m/ J, V1)可变参数的存储形式.+ A. R1 F# u4 a1 @! @
- N q' W+ R0 k' J- R1 H7 `
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
+ h3 e2 C) T) P' U$ @* _存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.. s3 e- R" i- P: C
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
& o" Y& e) A6 Z) U3 U E这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.7 B [; Z K! b! n5 }) L, K/ n- @
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
9 {9 F: \# u2 ~+ S$ V) D. a% D" U栈区:
* [! @& T# E$ Q! x! J$ M3 b. a9 |) r1 J$ {* {5 N# g' @; T
|栈顶 低地址& H8 `# p. Y& L- u
. h& N- G+ F6 ~+ [|第一个固定参数var1& ]/ X7 B K" j. d1 n' f
|可变参数前的第一个固定参数var29 _) E8 V/ j. W `# X
|可变参数的第一个参数
+ ]9 Y9 C J4 b3 o|...
9 h' T2 z9 J5 b5 {/ v# p' H|可变参数的最后一个参数
" e+ j8 U" r" M" @ c8 y" h: h|函数的倒数第二个固定参数var38 E5 u% U# q1 h5 [3 F
|函数的最后一个固定参数var4- [( L2 i, }2 Y4 ?9 r/ c9 V
|...9 G" P4 ^( X; M9 o1 P
|函数的返回地址! l' i# P6 q" v
|...1 u# C" U% u: U+ `% \
|栈底 高地址9 ^! y8 Q7 S/ u) e3 L7 n
0 N) P. c* d' t/ Y8 M
2)使用可变参数所用到头文件和相关宏说明
9 N$ ?2 }+ S7 n1 I: [6 B+ [& M: @1 A! B' q8 `( X l5 H
在此,以TC2.0编译器为参考对象来说明.& d9 R/ a h/ F
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
" i- x( e% t9 B7 m) ?此文件为:0 |7 }% N; S$ I6 E2 ^2 m4 d/ a5 g2 p
/* stdarg.h! J# h. d! W: l5 I/ F5 y) |2 m
5 u' o8 } I, ^( O& l* {7 Z: L
Definitions for ACCESSing parameters in functions that accept" x/ q; v2 C7 s$ o* E( b$ `8 x
a variable number of arguments.
. m& w, l: p- J- g
* S& K! ^4 ~5 _! W/ U" }Copyright (c) Borland International 1987,19889 n& o. n+ x4 W- T* N
All Rights Reserved.
0 `9 X4 N* e! o0 h7 t4 V*/# W. M7 ^& P0 F. A5 G* [
#if __STDC__
6 ?4 \. S& a/ t# b. m& a#define _Cdecl
8 G6 t6 q J2 z* r- r: x1 F! p' w. b#else
4 \. Z+ D4 L" y. H#define _Cdecl cdecl
2 q( a! {0 C3 X- K8 A$ \#endif4 w9 ^( O# T r) q/ A8 ]) Q
* T1 D9 z2 q# g, P" E
#if !defined(__STDARG)
# M+ R0 E- c$ R; B! u#define __STDARG
- k+ K. F8 \3 o: ^8 `* ^. z; a1 q. E; z Z
typedef void *va_list;/ D2 a5 |) O5 _
6 |8 G) V& s l+ P, A#define va_start(ap, parmN) (ap = ...)& b1 q2 f0 O2 P9 k4 ]. @
#define va_arg(ap, type) (*((type *)(ap))++)& e0 \6 E5 {0 I1 D- H
#define va_end(ap)
: A8 _; B, c3 @5 Y9 M#define _va_ptr (...)
/ Z% [; M' e; i6 ]5 Y4 C#endif
& k$ j8 a+ l# }! U/ ^! j; O0 C
- k. E2 _7 T5 q' {( S. W以上为"STDARG.H"的内容./ G* f3 u5 l) X- d0 L5 M+ `5 h
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;
$ J0 {4 K& k3 i* yva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ m+ r7 ~- L, U; t2 R, T( o# D- |
parmN为可变参数的前面一个固定参数.4 a6 Y4 N: G" W. F# R% s
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.) a; f1 C9 z3 j O
va_end(ap) 结束可变参数获取.
# W% v" @8 z. }- h- C) c6 i/ o& p8 f! N) W; j7 q$ ~! q- k0 U( k
3)可变参数的使用实例: J0 I: h; `! R$ Y' W! f7 y$ S
, _' P6 Q% i: ~" U1 K3 ~实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
3 f- S0 P+ n! D! s }& ^$ C; F
% X$ c* o3 F H1 C' e* f1 P5 q#include<stdio.h>6 R( c; Z) I; n1 B0 {
#include<conio.h>
6 C+ N: a4 A+ V% R#include<stdarg.h>
% R4 o. `2 M9 k6 ~void tVarArg(int num,...);/*num为可变参数的个数*/5 x* @* g3 [5 h4 I
int main(void)/ T, B; a5 i1 {8 x, j
{3 {7 Y a/ t3 b/ v1 L7 k* u1 g
clrscr();: O$ Z% G5 k( F* @9 E
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");' i( e: k$ J& F3 g
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");% I5 P1 s5 o. O1 x
getch();
* g' U- u d0 R2 Sreturn 0;# H7 f' g X" p+ D5 f- n, t
}
+ E- b" G C) M+ J- svoid tVarArg(int num,...)
, R. Z" \, u- t5 \( Q5 j2 l. }( W{% Z- c; v3 i+ c
va_list argp; /*定义一个指向可变参数的变量*/
) n s7 v5 S! u, ^va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
- n s( U# x9 N0 ? s2 j+ Swhile(--num>=0) {% w2 U9 R) o* U' R! C
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,/ @2 @) U H. ]2 Q9 G8 n
并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/5 h( t) o/ q9 {5 U; H1 g
va_end(argp); /*结束可变参数获取*/
. z, B$ b$ @* _; S2 ~) m0 |return ;) }$ Q& l( ?1 ^6 T( `' ?7 S0 e
}8 X+ n6 A3 o* G. ^7 a2 U
$ Z! c7 Y9 c$ m. [2 H4 e% y" b
4)可变参数的使用需要注意的问题8 L [2 V" E; I5 u9 I+ m7 \- h
5 q/ V% S8 `( c6 n# p6 R1.每个函数的可变参数至多有一个.5 t% c! Y; \ w- }. e. C' c' O
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
* }* E: a9 u2 l' l' h3.可变参数的个数不确定,完全由程序约定.2 @" L7 m3 c! e8 o4 }
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
5 @& T- V) q+ c4 J3 D0 F3 X而printf()中不是实现了识别参数吗?那是因为函数 + C+ U: g4 t. f; a( L. s
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
8 O$ G ]- L0 T的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ A( D; U7 G! g% i$ l j, m. g; S过在自己的程序里作判断来实现的.
, M' i3 A! i0 ~ d+ k5 T5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|