获得本站免费赞助空间请点这里
返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
  `! M/ q: a# z9 U它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
* a) m  T7 |' e! @. l- |% M( f可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
$ d' X7 \% v7 ~- z5 g3 C+ C5 c定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
6 o% ~7 ]. U. p) E实际的名称与之相对应./ h3 ~. U, O2 Z; W7 w, T
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.& u9 o  @6 w4 |  v
然而,更多地自由,同样也加大操作上的难度.& ^2 ^: a) n4 `. B( Y7 }' d
以下就对可变参数的几个方面作一定的介绍.
9 e5 h" [" s# ^8 Z$ E$ L" v$ T% l* _
1)可变参数的存储形式.1 A1 n% F5 L6 w) d. G* m

. l; B8 j' x5 I. p* @2 a大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
5 O% v5 Z! j/ y- M5 ?存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.& z/ ]) X4 N1 B9 x# Z+ f; B+ h
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,' Q' C1 ~- g0 ?9 K) `* G0 m
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.& p- E+ d  f! Y* u8 E4 k% i2 [
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
* v( @. c% W# ^4 O) B栈区:$ O& K) Y* m+ @3 i, V8 _0 P) e

! u$ c3 |: E' I/ j|栈顶             低地址
7 d8 @3 W9 v6 K  _
6 g. ]& x. [4 K& w7 X9 h' ?+ J|第一个固定参数var1
% D" w6 M; i8 d7 H|可变参数前的第一个固定参数var2
& f7 `4 @+ w2 l|可变参数的第一个参数# f# C; z6 A7 S+ m8 F* {  u- y
|..., A; f5 ~- [" r  G3 s! x5 O
|可变参数的最后一个参数
2 {; r3 K) z0 }/ A8 s! {|函数的倒数第二个固定参数var3
- d2 k2 G! B& v& K( E, G( ~. @3 T|函数的最后一个固定参数var44 W( l& e0 v+ y! Z, i. Q
|...9 G, c- O* }3 A; M; |; X# q
|函数的返回地址
- h& Q3 d" X; ?|...  Q. w. `/ ^6 Y, x: s: R
|栈底    高地址& a3 X! m6 t: |3 e9 ~
! i, ~( d/ H+ F. J) ^* A
2)使用可变参数所用到头文件和相关宏说明
! a. H$ G0 ?3 H3 k  ]! H
5 Y5 O$ R& g( K" C9 |+ h, }在此,以TC2.0编译器为参考对象来说明.
* P! e; ~: P- O2 @$ f4 F可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.6 g; W# P( \: y% Y
此文件为:
1 O% ^" V8 V6 X  v4 o$ h3 q/ C" h/* stdarg.h
9 e6 i, Q6 N2 M$ U/ s. Y! V% q  g: l6 h' \2 T" T. p  O
Definitions for ACCESSing parameters in functions that accept
6 a7 a" [; w5 ]: x( Na variable number of arguments." e" n0 u( F8 O* U- r

; Q: o4 G. j' B7 ^. zCopyright (c) Borland International 1987,1988
+ m, D# L8 F8 X; R+ Z* U& bAll Rights Reserved.6 V: J" L6 b, G+ i
*/
; m3 r% d$ b3 r6 x( y4 f; m* v# d4 e3 k#if __STDC__8 N- m0 ^8 A3 E' ]- }
#define _Cdecl. O) g3 z. E. O0 A9 s8 N( ?
#else- m1 n" r% I6 i5 `  L4 f& p
#define _Cdecl cdecl
# x% w) D! _  `+ D& @1 t#endif
5 }- J% L8 i2 A0 @* F- p# Z: x* ^0 k4 v
#if !defined(__STDARG)) \. Z- M* t7 i( ]4 ]
#define __STDARG
- J+ P4 c- V7 p1 r
4 v2 j+ n# h; I# {4 atypedef void *va_list;& W6 B/ c# \9 w* N. I1 q
$ V+ m0 V+ g/ r. u3 }5 _
#define va_start(ap, parmN) (ap = ...)
% E# K  Q5 O  u0 H& B& _  l#define va_arg(ap, type) (*((type *)(ap))++); h5 L/ P+ [  [
#define va_end(ap)$ a! s* z* S& P3 S
#define _va_ptr   (...)' X( y# [: d8 C
#endif
8 C1 K0 L7 ]7 Z1 ~! q) |
/ ?% m( Q0 L  |  S1 Y0 c" e以上为"STDARG.H"的内容.
; K5 ]- ~' h: T: N该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
0 T* k: T# X0 S  d9 F7 Fva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
- W9 S  E# T8 H2 r' ]9 iparmN为可变参数的前面一个固定参数.
" L& v$ \5 w" @va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
3 I( C. T$ q' d' E& g& s5 O; q1 `va_end(ap) 结束可变参数获取.2 H7 r7 j3 G8 B+ {& [

! S; w7 h: e/ [- p- Z9 x  Y9 b! r1 i# H3)可变参数的使用实例# g  u9 J5 g. c* }

" z* X/ U- A, {& m  ]& o( m8 K实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.. o" \! ]' H2 n1 A4 w3 B6 i( _
5 i8 ]8 Y( r# `9 c+ S' n* C8 R
#include<stdio.h>/ K! |3 L! g3 c: h. |% i
#include<conio.h>
# k2 Y. M, y1 E; L& {3 j5 F% \#include<stdarg.h>
% g: j" z# \$ z  p% |void tVarArg(int num,...);/*num为可变参数的个数*/, z* u- r5 B/ D" _, X- ~
int main(void)
! M% r* z8 M8 d3 J' o/ c{
/ S+ A; g. F' Q3 Y+ ?, M- Jclrscr();- X" {  ~2 b* X2 b# H
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
) D: Q! z  i. L% J" MtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");: Z3 t6 o7 m$ k; l# j8 s
getch();
% e/ v# U8 ~; u: n; m$ O7 |2 H' c' Jreturn 0;+ p& v8 v" P& |( T( w& U
}
/ J% q0 ]* T" _( ^) Ivoid tVarArg(int num,...)
1 T" L1 j/ R0 z% s0 S: ]1 s  m{
* k3 [2 H' n: C1 R. o) n5 F6 vva_list argp;  /*定义一个指向可变参数的变量*/" x3 z3 Z, O; @& Y5 U! r
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/6 j6 i7 H! u" [: z9 a
while(--num>=0)
. c; k# a0 x1 b2 @& n  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 ?7 r, z5 N  ~' ?* t    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
4 N4 ]9 H) s6 ]0 Hva_end(argp);  /*结束可变参数获取*/
9 u) i7 P8 n+ J4 B9 t% X. ^return ;
! f2 z8 K7 X  c2 l* {$ G1 v1 a* K}1 `. T3 I2 d+ F# y
$ D) Z; s/ @# K% q
4)可变参数的使用需要注意的问题
  e8 D6 m: ~$ w$ t: t0 ~5 x' W
2 \+ D' I$ t/ M5 v' S  j) R" v) s1.每个函数的可变参数至多有一个.
3 b. }' S7 s9 k1 H" n: \7 W2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.6 S) i' Z- k+ d  K4 e! A
3.可变参数的个数不确定,完全由程序约定.
! c: C6 _( d  A0 f% N" a4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.1 o1 v+ \# H! e2 R
而printf()中不是实现了识别参数吗?那是因为函数 5 F1 b# \" f% Z- x2 H, o1 E
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 7 B0 @) o% d: w! f# G! B1 A
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
/ E& P; \; z5 F/ D: F9 J; Q过在自己的程序里作判断来实现的.
0 O% c$ j7 Y+ m/ L' ]& t7 ]5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

返回列表
【捌玖网络】已经运行: