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

|
函数的可变参数详谈
可变参数的英文表示为:variable argument.
& F [/ A" _" p8 L/ z6 l: g0 a它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.! _# I% E6 t+ \7 U3 q
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
- v( n1 l- |* {8 v8 u6 i3 D定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
8 ^5 x2 c3 B: \: \: o. l4 Y* g, q' e实际的名称与之相对应.
& j& J: S, c2 @4 p/ J, |+ Z& @& ?由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
: y( [) L) M6 Q% a1 J, V) I! n7 a然而,更多地自由,同样也加大操作上的难度.3 p! j9 |7 g; g3 ~5 v. G5 f/ u
以下就对可变参数的几个方面作一定的介绍.) O& E% K( O- Q g; n
5 x6 D" s4 t' J4 y! q4 D
1)可变参数的存储形式.4 `: z7 z5 t% V$ I
7 }! M, h3 `1 b( p w5 u+ M
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
$ r0 m6 D; R/ q! a存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.3 n' W/ ?1 {4 d
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
# n) o' s2 b* B7 D: }. j- Q9 l) j* K* D这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
8 g# X: f* ?) J+ |7 A8 ^; ~因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):) v# X6 {" ~& V) N3 {0 F
栈区:
8 U/ {, H N% @: |1 C6 V, I5 ?8 N' G% f
|栈顶 低地址5 k+ W) V5 E, T0 ?+ Q1 d, V' `
- k1 @) H& _/ k# {7 W$ a9 K
|第一个固定参数var1& `, a: d+ g* `, V: V
|可变参数前的第一个固定参数var2' R" n7 b" k2 q, m* c/ \
|可变参数的第一个参数7 w" x0 \, p% U7 b% E, Z+ g
|...
- @+ Z. k& p. H8 y# {1 i+ M2 T- q|可变参数的最后一个参数% {! ?! j* i: v5 [1 B$ T5 T2 t2 F) t1 J. r
|函数的倒数第二个固定参数var3# f& Y6 v- @& K9 {2 b0 h
|函数的最后一个固定参数var4. @$ C; k! u- f, ]' [
|...
5 ]+ K$ W* T# }6 x( Z8 N|函数的返回地址2 d, H' K* [- h& Z- f) C4 ^) _
|...
0 {+ {/ r/ {3 M) o2 p/ m5 _* h|栈底 高地址
; N( E6 F! f2 o- J8 q; ?0 ^+ X$ J/ l
2)使用可变参数所用到头文件和相关宏说明7 S7 X* d- O! L2 s/ j$ k5 c
* c3 H% f( V1 i% n8 Z& ?7 e" K
在此,以TC2.0编译器为参考对象来说明.$ c8 J. F" ^" z6 j0 X
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
$ y p/ g: A& Z; H6 ]此文件为:, p4 d2 Y) Z( w2 c6 w" p# N# U
/* stdarg.h
7 M( y( r+ G1 J% P
* P& ^" `$ x* C' {& N! RDefinitions for ACCESSing parameters in functions that accept
! V( J$ T2 k" q' sa variable number of arguments., x$ ]' s: e4 ?* P, q4 y
4 X( c4 a( [& |6 |6 ?
Copyright (c) Borland International 1987,19885 e7 c3 i) E% k: u0 U" x
All Rights Reserved.
) e2 \6 {; h' j+ `3 S*/7 S% t, q* ~. j
#if __STDC__
7 d5 h, q: J1 Q% R" c4 A#define _Cdecl
. i" |5 R3 d9 e. z#else
2 }2 I' O+ u \) M# {#define _Cdecl cdecl
' L6 {2 E* @4 `6 P! A: w#endif% l' S1 X, @. n' [
; m8 \+ a7 l$ y$ L8 K2 c* `% c#if !defined(__STDARG)0 T0 z# _& Z) M! h4 L/ ^: P
#define __STDARG
/ h% E- P2 T: C( m! P
F1 `# }3 F; A* M. u* @$ ttypedef void *va_list;( U3 F# E! _# w' v+ R
9 l' y9 ^7 i) V/ m1 I0 T" }#define va_start(ap, parmN) (ap = ...); n; {& l9 P+ `8 h: w6 m3 L5 B; L
#define va_arg(ap, type) (*((type *)(ap))++)- }+ d, b7 e" K; W1 `- ? \( i
#define va_end(ap)1 R. j; G5 L) v6 Z; O
#define _va_ptr (...); M# g" w/ l% |& K* Z
#endif. J( ?9 \' y, C
& `. A5 h" z! k- t; ?. g7 f7 K/ T' }
以上为"STDARG.H"的内容.3 X0 m1 d) H5 Q6 U6 ?7 s7 X3 }2 N
该文件定义了使用可变参数所用到的数据类型:typedef void *va_list;4 W. ~- H O! a1 P% L1 f5 `
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ @/ ~% u* @5 T, T
parmN为可变参数的前面一个固定参数.
9 p$ I, ?8 E- }( jva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.) x) Z) k: @- A" |4 J
va_end(ap) 结束可变参数获取.
; u) `3 I$ G. G6 p; V% Q, I% D
3)可变参数的使用实例0 ^" j! l9 G: p4 i, O
2 a$ n3 `, G) F: `
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.# k) h, [7 M3 f! C) ?/ S% B
0 t0 R4 Q6 p9 F, H \- |" Y#include<stdio.h>
2 t% Y# R4 J& ?: v z#include<conio.h>
$ c5 f+ J- f$ |4 a# i#include<stdarg.h>3 g4 l6 E) ? F5 h ^) y+ M2 }
void tVarArg(int num,...);/*num为可变参数的个数*/5 ]* e& H% Q; _7 @
int main(void)6 K4 s3 N/ s; U2 R/ {4 {
{% x$ k, z( B j( m& l' Q
clrscr();
+ M: [3 V; w* ~" h3 Q ttVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 H( G% [9 ^* ?4 ~" g
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
Y( Y! i8 O. Jgetch();7 g7 }& y, O O. \# @
return 0;
) f# L+ z3 v- R% F}9 G* y' e7 M/ o) J# O5 O
void tVarArg(int num,...). e+ z v: G9 s, x- f
{# V) n: d8 G7 o/ X
va_list argp; /*定义一个指向可变参数的变量*/
( {1 ^, |; S9 Dva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
. |$ d `! d6 h6 e3 cwhile(--num>=0)2 F! n3 A) ~, Y
printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
9 q8 w6 H ~8 J3 L4 ]5 d 并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*// s; w9 M% \1 P! T. k2 v
va_end(argp); /*结束可变参数获取*/
6 ]9 j+ ^5 F9 yreturn ;
3 N2 Y" p7 q; k) J4 g+ f}
/ s! H! N* _: F# M1 y) Y4 F v6 Y( N# L& `
4)可变参数的使用需要注意的问题
7 e3 I. n3 ^7 C: e( n
( M6 k! i& L+ c N% x9 ?1.每个函数的可变参数至多有一个.2 J9 c) G3 k( n& ^
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.! v. ]: N. F( j, o- P# f
3.可变参数的个数不确定,完全由程序约定.
$ O5 A" V5 u7 W4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.$ g! n" ]6 } `2 c3 y' n0 X* ^
而printf()中不是实现了识别参数吗?那是因为函数 ( n9 J6 ^% g- t; l
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg ; }7 M2 G* o4 v1 F
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
# T; `& S" r8 P2 e! G) n- {6 w过在自己的程序里作判断来实现的. ; G, I3 v& ?- F7 a: S
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高. |
|