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

函数的可变参数详谈

可变参数的英文表示为:variable argument.7 U) P! B1 i2 W$ O  U1 p. ^" s+ T
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
4 X' B3 u8 R' O( ~! K: G可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
7 X7 G+ H1 Z  l( [6 a定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有; A. @: k' U& y/ a6 I
实际的名称与之相对应.
! w" O8 K8 a4 J1 F由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
8 o2 L" g$ z6 [* U/ Q然而,更多地自由,同样也加大操作上的难度.
0 y% \* P3 i3 u% R2 f以下就对可变参数的几个方面作一定的介绍." T* y/ N6 v$ F8 k) C' W6 E

) Q' ^6 E4 J7 p6 U; s  i1 y2 H1 u( Q1)可变参数的存储形式.
, Y3 Y4 v6 Y* y# Y4 ^% Y5 O5 y- N9 r3 g" C; F+ l( q
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,$ \" p9 K, D* N! ]; Y' ]4 z
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.- R0 w9 b6 l  \6 G) c- w* i5 H" n
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
3 `5 t) Y6 o: j* @0 s/ g( H这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.1 v( V( D; F9 V9 @: \# p
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):8 h0 ], i# W4 X. x5 e
栈区:' ?6 i% T) {2 _- B3 b9 F' Y# l
5 n0 K$ d# d2 ?! V& M# O- R1 p
|栈顶             低地址+ {; K3 e( m" _

; v  F0 v- {$ y8 K3 q( W|第一个固定参数var1
$ H8 V" S+ N( Z. R9 v|可变参数前的第一个固定参数var2
2 q5 k  A; t& I! w8 u|可变参数的第一个参数
8 [8 v5 m" R) d8 D+ c|...
; S1 A) J$ \1 K|可变参数的最后一个参数/ b0 F; m5 i  }+ R2 I; g! l
|函数的倒数第二个固定参数var35 }) h* b& K7 B7 b
|函数的最后一个固定参数var4& z$ v9 c* e% f# T
|...% J$ v. j/ w' s+ o9 s
|函数的返回地址& \; P; B) L1 n
|..." W8 `( Y) ]3 \9 N! Q% V4 U; e" _/ S
|栈底    高地址* J% M/ C  ~) E8 S3 ~
/ v: E/ E7 X' |3 R. M+ o
2)使用可变参数所用到头文件和相关宏说明; x% _) u# c7 A" R3 x' m
. d4 e& o5 D% S, f
在此,以TC2.0编译器为参考对象来说明.
4 q) [1 a' H8 d5 X! U& D可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
% R9 J, Q3 ]3 e) T此文件为:
6 s) q* U" A* L4 T6 J/* stdarg.h
" t9 M0 c5 q  ]9 M+ E% A. J* k9 H( j1 m7 q2 `4 u# B
Definitions for ACCESSing parameters in functions that accept
; y9 g) F6 u! Z: W* H, a* {a variable number of arguments.
% c9 s9 z1 B3 M+ `4 w  H5 v  _) j, k: n1 I# K& Q
Copyright (c) Borland International 1987,1988
: s6 O' R6 k) Q3 U3 n" `7 _All Rights Reserved.- {: g: W6 a: `( T1 `
*/
+ G5 {. A. T0 p. J+ X6 r1 x; u( x#if __STDC__3 V8 ?. ?9 c/ f, a! I
#define _Cdecl: R# N1 S! Z( Z" |4 f
#else
& J# Z! m2 U9 S) O) T! n" [  d#define _Cdecl cdecl
5 G- H* {! V) h* n' r# B#endif
* p! t  Q4 p4 F/ m3 l  ^* k: w. C( D8 Q$ G0 u
#if !defined(__STDARG)
: D7 K' W% o% Y0 D* Y# d7 `#define __STDARG, o9 o1 t) G6 e) J! _
! d7 a# i- u% ]1 C) b
typedef void *va_list;- f, V9 S+ @7 \7 s+ R! s1 X
% \9 ^& G$ U6 g" O
#define va_start(ap, parmN) (ap = ...)
4 g/ {* m5 U# v, _* Z, I  F#define va_arg(ap, type) (*((type *)(ap))++)
4 a" g8 A! G7 i1 {& g#define va_end(ap)) f$ r" X; {% i' y
#define _va_ptr   (...)7 r1 V- J+ q2 p! i) W; B) e+ e
#endif( x2 ]0 N- x2 c0 j' D1 S/ g
# T; O( z0 v" Z7 K1 I, |  y7 R
以上为"STDARG.H"的内容.: J; F8 p, d$ X; m" c
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
3 {$ H( X# _- V2 D# v  }va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
: z4 W: Y% s1 |3 t8 f+ H: JparmN为可变参数的前面一个固定参数.
9 A, ^$ l# L) ^, Hva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.4 D/ ]) R/ H5 q6 R- E) ^
va_end(ap) 结束可变参数获取.4 b+ y# B( \$ X. d

- Y: s. G% g/ @. Z4 j5 ]8 U3)可变参数的使用实例
! ]3 v0 n8 ~+ c9 K1 c( p0 Z
/ {; h& M, Z- K6 {" s& x* e实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
) Y4 j+ L" P& [# }/ E& W$ l: ?  ^1 x. J. a8 Z9 P* C
#include<stdio.h>
2 b& Y/ D# p- z, \#include<conio.h># q6 F4 _2 z8 ^) c5 x3 m* |
#include<stdarg.h># |7 H4 f5 J( @
void tVarArg(int num,...);/*num为可变参数的个数*/% U* I- K3 _8 Y' t0 U2 B
int main(void)
/ h. z+ e" S- N- k4 |{
6 d) z! f8 O8 G4 oclrscr();
& h0 `/ b9 Z0 p/ t4 O- _" ktVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
. v0 S! v6 T1 mtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
0 t( A* j0 Q: ~1 F" ogetch();7 b5 Q* A  _$ c' v: B. p0 U9 x
return 0;
6 H6 R8 }, G& R" q}3 y$ L' Q3 |  v& u9 ?# t
void tVarArg(int num,...)9 {0 ^. K+ m* ?
{
+ d8 S% i) [' \* O) Y# _* d$ Yva_list argp;  /*定义一个指向可变参数的变量*/
, }! k! y: O* w' t0 b0 k# p% _va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
; ~" c! n0 x$ V# a1 m& I4 Q$ e+ ywhile(--num>=0)
9 y. S( U. |4 A& S2 |  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 F. O9 a5 R+ p    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
0 f5 P: r4 \5 n3 F  w! D6 q% A8 b( \va_end(argp);  /*结束可变参数获取*/
* P% h* F. a7 a4 U, [! d  h8 _/ Mreturn ;
1 @& E& Q7 n  {8 z}
# t6 l; j! G( X
$ b& A# T1 X2 ]4)可变参数的使用需要注意的问题
- R! X: c( `; K; f6 {5 d
- D$ C3 v! l+ B% _$ Y1 ^1.每个函数的可变参数至多有一个./ b: t8 N' p- n* {3 \3 y  E. d
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数., Z- S) [8 c0 \( c" s5 P
3.可变参数的个数不确定,完全由程序约定.* O0 d9 Y" s: ~# `/ l- k
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, Z# Y# {7 P8 i4 n' k- s: J$ U而printf()中不是实现了识别参数吗?那是因为函数
7 a" h! _5 g/ w4 _& Q6 fprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , s" B5 r) [& E2 b
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
$ B3 s4 a  e+ }2 `9 L' M, V过在自己的程序里作判断来实现的.
  b0 w- ]7 v4 I5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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