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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
5 B0 K8 N" ]9 w+ Y) d$ n它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
' e! ?0 ~- x) g可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不( k: O' E# v9 f: ~4 S
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有8 S* ?) u2 w' ^5 K' ~# N  U, t
实际的名称与之相对应.1 o& s0 b* M5 H) l- F
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.# R: d: d1 H8 `, I7 v
然而,更多地自由,同样也加大操作上的难度.. m3 v9 L& Q, K2 B
以下就对可变参数的几个方面作一定的介绍.
0 Q! x0 B9 F+ P2 P: m
2 }- D8 C' b. L  g+ h1)可变参数的存储形式.
# A' Z  o' O9 W/ Z' m9 L2 p- K5 w1 G/ Y/ K, {% f) U
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
& C) P$ j6 N8 D存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.3 Y3 Z+ v# h, m) @  [3 P
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,7 P& x  R& V+ C2 X% o) f8 e7 C, |
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
  w3 D' V# m' j7 b% i, b+ Y因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
4 j, l7 r9 B1 C/ Z栈区:2 I1 H' J0 ], b
4 K4 k8 f( G5 e* l# w
|栈顶             低地址4 R" S, c9 \7 Q1 _. k5 N  u- w

. _6 o( w! m% j0 e  h|第一个固定参数var1$ Y; U$ C2 N$ p9 p2 w: f' ^2 h
|可变参数前的第一个固定参数var2
( _) [  o2 f% N& \+ N9 q|可变参数的第一个参数$ [% Y: G+ `, G6 a4 J) v
|..." M2 N9 e) r$ ?: q+ _* K
|可变参数的最后一个参数% X4 }0 O3 ^: A5 j; W5 y# x: ?
|函数的倒数第二个固定参数var3
! N4 O. q! C" p+ B9 `4 X|函数的最后一个固定参数var4; C+ W- H+ H/ F8 U0 }
|...
" D0 h: s0 @* ?, _2 Y|函数的返回地址. A. ^8 J7 W" n5 |7 m8 S
|...
% r% r/ V0 w" n: M4 o: a# D( D|栈底    高地址
* a, g7 I: M5 y( P
1 P  e9 z* k! P, k3 {4 e2)使用可变参数所用到头文件和相关宏说明+ _& d. H- B% H$ H; J
. s& X$ c" h# E: ?  x
在此,以TC2.0编译器为参考对象来说明.
; d6 y* S8 S) c! A" L可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.  I# @' l* m8 t6 R
此文件为:' R7 j0 z! _  l* @( e9 x6 Z& F
/* stdarg.h
/ {! @, K; D0 `0 F- L& _
9 K% d' f6 J  q! x' _$ ?6 I' t- {$ ?8 KDefinitions for ACCESSing parameters in functions that accept+ {7 k$ }1 b, V5 g5 j1 M7 j5 a6 v
a variable number of arguments.
+ ~  z+ m* u2 ]
- y: q4 {2 _* z0 x0 W/ s% G+ ECopyright (c) Borland International 1987,1988
1 Z' r6 t. ]8 ?# c: Q4 \All Rights Reserved.1 @: f9 e2 p% o! f
*/
/ D/ D! q5 ~0 _#if __STDC__7 x& V- I" a3 }4 P) X7 N/ I5 i
#define _Cdecl, w/ q0 s- z- a0 d
#else' l4 d; `1 X0 X  W1 d
#define _Cdecl cdecl
; d3 _$ Q3 @6 q$ [#endif
. R$ ?3 N8 Z2 U, L
/ U$ [6 j4 a" K, o- M#if !defined(__STDARG)
- `9 N  B0 J) c5 \5 Z+ O. J#define __STDARG
# s7 E, Y9 Q2 l% q5 O* l5 J- y! d0 }/ [, I0 ^5 @
typedef void *va_list;4 w; B! Q) Q+ x* n0 v6 x

! \$ V- f6 R& n9 Z. Y#define va_start(ap, parmN) (ap = ...)9 L' g' ?* F+ Q' T2 g: h
#define va_arg(ap, type) (*((type *)(ap))++). F$ a/ ~2 M# }
#define va_end(ap). L+ N+ h: @6 y# g: {3 P8 W! T' _
#define _va_ptr   (...)  k0 q% N8 Q5 e
#endif5 B8 @, L- Q7 F) A

( B- b; f6 J0 V& `+ j0 }8 ~. @以上为"STDARG.H"的内容.. ~& R; K6 u  a+ v+ [
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
" [: Y+ d! {3 d! Sva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,8 O3 `- B5 _) ?7 h- K8 h7 v
parmN为可变参数的前面一个固定参数." a: Z# `5 B2 Z0 s, t& [
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
6 x3 T8 n5 p6 W, j, Tva_end(ap) 结束可变参数获取.1 b7 j4 u$ ~: x4 H7 l

6 ?/ T$ Z$ }& I. D& h3)可变参数的使用实例+ g6 S. ?/ o7 K* ?- }5 ?

2 |$ Z6 o+ D! a/ t实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
$ Z0 W! f) J; m/ ?2 F
2 t$ l: X. m# }6 P# f#include<stdio.h>
0 ]( @' g! Q$ g- B#include<conio.h>& g* {1 n! H8 T" }
#include<stdarg.h>& v- i6 d! X/ t
void tVarArg(int num,...);/*num为可变参数的个数*/
  O3 j. c3 I  o: ]" n" R, [int main(void)
% R  _* [$ j; `. f+ M- E{/ u2 F" G$ d& h# g! i) }" C
clrscr();
# k- c+ h* a9 _tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");, t  ^& z, `9 l; T3 U2 l
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
! q8 o' g8 P& R3 Igetch();/ d+ ^. d* ?! h4 g5 P6 |; N
return 0;) M  M8 f9 I; }( d
}  k  c3 Q8 N# g/ }* m7 b
void tVarArg(int num,...)
5 {: d9 f* Q: D- i7 I{6 _" ^6 t) I% z3 F- N/ h/ `
va_list argp;  /*定义一个指向可变参数的变量*/  k0 j8 w( Y. n$ d- _
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
: X8 p; Q" ?' Qwhile(--num>=0)
' X- t; e% D5 C* ?6 u& Q  I  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,! x( ?' ?6 p% O
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/0 Q5 c3 Z- N5 ~* T
va_end(argp);  /*结束可变参数获取*/0 ]+ `' a" u% A% O/ }! z
return ;* t' ?% W5 e: l& W$ h
}
, ?8 e! w0 e& ~7 @8 m
: R! ~) h9 ~# e) W6 a! x4)可变参数的使用需要注意的问题
4 D7 P6 `' x7 @6 N  G
: H, X- Y$ V6 X) L' f! [' f& ?1.每个函数的可变参数至多有一个.
3 j% T3 n; P5 m$ r2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
' I+ {# b" K) L3 ~$ Q9 s1 b3.可变参数的个数不确定,完全由程序约定.( Q: k8 D% c  }3 c3 M
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
5 e! j# Q2 b7 N/ L0 C# s而printf()中不是实现了识别参数吗?那是因为函数
4 E; p, V5 F8 S: n) X* o! Jprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
; J' Z# T) a& I2 ~3 g的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 # L: m% Y% ^* C, {
过在自己的程序里作判断来实现的.
' r7 n' U6 L7 Z5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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