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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
/ }! S$ `$ E6 f( G" G: O) l它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
5 W5 r5 I1 O1 m可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
- R, N1 F. O k定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
9 d Z2 E" j% C! G, a2 B实际的名称与之相对应.4 O4 `! w5 B8 P$ M# T, i* c
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.8 g, X, U5 O6 J& Q
然而,更多地自由,同样也加大操作上的难度.
) D6 o+ m, t0 q2 L r. n以下就对可变参数的几个方面作一定的介绍.
: y3 E5 B: W: e0 D
! s" V- z ` A4 Y9 ^8 |! K- ]1)可变参数的存储形式.
3 J9 l& G2 X4 V. }# q2 I' e. ]4 }6 H
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,% w, X5 C$ p" B$ O# s* M
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
2 ]" b# S5 U; K+ C. C" j在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
; v2 N0 ?. Z. K4 _. h4 d! G这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.$ [; n1 n7 _/ T7 o% S
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):0 j7 t' |+ E0 y# [$ D9 ?9 Y
栈区:
8 \& P# p/ V, j9 Y4 O6 f2 m: x% z7 k. u2 c/ a
|栈顶 低地址
, f& c# ?1 S$ f/ p
$ v" H3 O4 f# w$ ]2 k! E|第一个固定参数var1
7 ?6 X7 s0 T9 L1 Q|可变参数前的第一个固定参数var2
3 w6 P0 r: f# X! s$ b|可变参数的第一个参数
3 n9 O1 S7 H9 E/ B+ n3 t. w1 Q|...
) }; ~: ~3 a4 z$ V* c* [& V# Z|可变参数的最后一个参数
0 ]4 d T1 w, d! F( z2 k/ b8 P# G|函数的倒数第二个固定参数var3+ _+ P2 \$ F2 i0 h( G. Y: q$ ?" u7 q. w
|函数的最后一个固定参数var40 q4 ]& I2 ~8 ?% C; o
|...
% d$ C. F% {' j: E0 q|函数的返回地址6 f4 r4 H5 n0 t9 W) K" n& z
|...& O/ V) Y, h% w, M2 R& c6 N
|栈底 高地址% d# Y: M, n" R' j1 L
6 c: D, g- v1 j8 j5 s0 U r2)使用可变参数所用到头文件和相关宏说明
U5 |3 z( Z* i! I. A" D& W6 q! d L
在此,以TC2.0编译器为参考对象来说明.' S1 J+ H* i7 G1 P: O& [
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
1 e& I9 Q* B; Q3 l/ p4 f此文件为:
: E( h/ R: t$ U+ c/ [& x% s/* stdarg.h0 H3 O2 {% c! L; U
8 z; i" M ~9 F0 R3 V2 z4 Q; S
Definitions for ACCESSing parameters in functions that accept
( k0 u4 m! ]. O! A& W& V, la variable number of arguments.
5 E1 E N8 r8 z/ N$ `4 O/ \) z' p+ p% c$ q7 y1 Y
Copyright (c) Borland International 1987,19884 d8 y( C4 I7 A4 X2 ?# \' `- `
All Rights Reserved.1 `! k. ]" u. t+ M
*/
! R( q2 f$ J& l& h" g) u#if __STDC__
. _5 }7 ?- ?% A7 e6 R2 h#define _Cdecl' q0 n( K0 w0 \. n
#else" A0 ]! p) i0 k
#define _Cdecl cdecl
L. q5 D: z$ \- n: O#endif6 Z) D" Y2 k6 W# |0 u
8 M4 w& ] w: s- Q
#if !defined(__STDARG)
1 b- H+ J! u) @ L% T- K8 j- w#define __STDARG
5 o; q ^# E0 I- ]) e+ R8 _/ L% J% y# p7 N5 G+ O$ |
typedef void *va_list;3 P& K! B, Y i, X0 k1 |
9 L8 ]3 V d0 h+ {
#define va_start(ap, parmN) (ap = ...)4 w8 e7 G! k3 G
#define va_arg(ap, type) (*((type *)(ap))++)0 u' t6 S; h4 o5 O2 Y5 x
#define va_end(ap)4 D) s/ _1 D% _
#define _va_ptr (...); L- N3 d0 x1 v
#endif% F$ }' Z, \& W" g% W* q
& |% p3 ^0 ?- u; k
以上为"STDARG.H"的内容.3 \7 }- W8 z8 r( ^9 T' z( [
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;+ t# F, j- W+ Y' B) z# `* F
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
' G# B% Q5 u" s" OparmN为可变参数的前面一个固定参数./ {0 `( @9 [3 e" u' X
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.& P$ M3 t/ Z4 L& j2 x
va_end(ap) 结束可变参数获取.
' u- Y1 m: Q* {1 ~) u6 K3 `9 x+ V8 A* q- g
3)可变参数的使用实例
2 D# V/ p8 U0 l: D$ ^2 L* E( B/ `4 w" }1 }4 g
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.: q: w. X: M- n0 j. `+ v
k) T L1 p' q. u9 \#include<stdio.h>
* B- f" K8 O% y#include<conio.h>
5 L! s( i$ T4 r0 ~6 h0 w' n" K#include<stdarg.h># U: k/ ?7 @/ V! G3 K3 ?
void tVarArg(int num,...);/*num为可变参数的个数*/5 `9 w! i- N/ |6 c
int main(void)5 {3 U; E4 @/ o- ~, y; ?
{& k5 a: c* ]* |9 J* p$ |- _
clrscr();
; I9 R5 O8 @" E) I8 S# u) NtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");2 n" w$ I/ _, T- t9 q( w* X8 D
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");: o# V& ~1 d) ^8 n: W- j' }* f }
getch();
5 e2 T2 ]! |6 d# ?0 V- R1 treturn 0;+ c# s4 }2 U' p
}
+ a6 @5 d u: y" P5 \: z2 tvoid tVarArg(int num,...)1 A: N" ~" {1 y6 T: O
{
; ]: B' M1 x9 |% Vva_list argp; /*定义一个指向可变参数的变量*/% e" [: n9 M/ R9 E: P1 j
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
, Y+ |8 ?9 Z" D& X0 Nwhile(--num>=0)
* V. C+ N* e0 B3 n printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
+ U% V- S/ |% t {2 p# u8 S+ C9 t 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
8 k7 U1 L# s# V+ _# K& p7 f1 Hva_end(argp); /*结束可变参数获取*/2 Z$ V/ S$ M- H. j$ y" j9 S1 q
return ;5 g( K" E2 o" r( x7 m
}' g {/ P c3 e" U
5 M- B2 c9 H" V. P
4)可变参数的使用需要注意的问题
1 A8 Y- ^. j X$ d$ b3 L1 v" R, N* C: r0 W
1.每个函数的可变参数至多有一个.9 h9 }; c. j9 M4 Y6 R9 e
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.5 \( K6 [- Y+ d' v, H, x0 i
3.可变参数的个数不确定,完全由程序约定.
% |# V5 x' j* h6 T w4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
/ X+ @8 F- r6 g7 r6 [% u而printf()中不是实现了识别参数吗?那是因为函数 " L; N o0 s3 L9 d' r) t f- f
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ! M4 \7 V1 ~" O9 d9 z6 H
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
1 k' Y! w3 [6 X2 E过在自己的程序里作判断来实现的.
* w0 w) d1 f4 m" Y: ~) d! o& ]- ]5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|