返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
3 R+ f, @9 e3 i它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.. y7 l+ h0 W+ n/ ], w
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
7 X( [) R- D+ r* b( J定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有& e" `7 t8 J0 O' B
实际的名称与之相对应./ T( Z( ~* d+ d
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.4 a! Z7 Q6 O0 j& _# I) e  A1 L; c
然而,更多地自由,同样也加大操作上的难度.- N# E) J9 v& K4 d& Y% g# k
以下就对可变参数的几个方面作一定的介绍.
3 ^, a# w% Y5 N- U; i$ T3 U  _& {; F; R# ^6 ~
1)可变参数的存储形式.6 b) z: L: o1 T

7 f4 y$ o) k7 `2 Z/ m5 @; p  c$ T大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
6 R% r. a4 b5 z0 ?: E存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区./ k4 p( B1 Q! p4 `" T
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
) g; o7 s' p) I& B3 f- _这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
: \$ D& h3 [1 r% \因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):5 C& p/ m$ O% l/ e
栈区:
3 C0 {, o  a$ m, X* ~. z1 O$ d' Q6 K9 K* _6 }' T
|栈顶             低地址6 @+ S0 s8 |$ D, K

6 ^4 m4 y3 ~6 J, K! l1 a|第一个固定参数var1
' i: l* I3 F2 r! c. J! Q|可变参数前的第一个固定参数var27 ]3 v4 y: b  ]/ E
|可变参数的第一个参数
. v& T. }* B7 F2 c# Y9 P" K|...
' \6 F; @- y+ S1 x5 m: }; n/ r8 [4 G# O7 l|可变参数的最后一个参数
8 m7 t$ g! v7 R1 E$ T|函数的倒数第二个固定参数var3, D; e* n6 `1 x; U8 @( C8 p
|函数的最后一个固定参数var4
2 P6 v. J7 t6 Y' L5 L|..., ~# t2 Y, _4 ^! h4 B
|函数的返回地址
* S+ K" t8 K) O- z% K|...$ G  S/ r: f8 L' k$ ^4 t
|栈底    高地址
3 F+ W8 K8 T" i; y7 Z" t( ^! S+ M/ u% U( x
2)使用可变参数所用到头文件和相关宏说明( `, B8 B1 L2 r8 E& h% ^3 _
6 n1 I) U, Q- M% e
在此,以TC2.0编译器为参考对象来说明.
& H. I+ v+ w5 O5 O8 A) u可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
- N8 u, ?8 a2 {6 t' U' A# d8 f& \此文件为:
. u( K. s' x" }0 I: w& Y8 _# q/* stdarg.h2 P  ?& @+ o7 _& d' @8 T, y

0 \0 N# E. n$ ~' W  U$ s" rDefinitions for ACCESSing parameters in functions that accept
' _8 U8 R3 B/ s# l4 y5 t4 \9 za variable number of arguments.. F" [5 b. [) X2 _  \. S, b6 u
2 r' t1 }; V& `8 ]5 ^2 `
Copyright (c) Borland International 1987,19881 |! {3 X! e. W. I% y
All Rights Reserved.
- I- s1 M  S1 w1 M7 ~- j! G*// g: e  z  Z9 k7 n& u1 Z- H( i2 X2 H
#if __STDC__- s' W) Q2 |2 ~2 L9 }
#define _Cdecl/ y- e6 X, N2 P6 G( o
#else
; ]4 }8 p6 q4 y& V& `' ]#define _Cdecl cdecl) q& g) k# T* c6 R' L5 Z
#endif0 w# ?' X5 H+ d" x" g0 E

: \- j7 v/ J  I3 P#if !defined(__STDARG)
, C* O- [) D/ e  S8 Q0 s2 B' h#define __STDARG; ^; N$ }+ ^& I8 e5 V7 S
% p( J. K9 F: e) o7 X( ]5 ]
typedef void *va_list;) E+ h0 b0 ~! r( E

9 _- k8 D, {: o0 r# q#define va_start(ap, parmN) (ap = ...)
: K( G; K. `/ `#define va_arg(ap, type) (*((type *)(ap))++)
8 ]( o, v! p0 Z$ j0 P% h7 A* ^#define va_end(ap)
, U: v# D. r8 J* G6 }! E( b#define _va_ptr   (...)% J& _+ X: Z( B( Q2 ]
#endif9 ~$ E3 R% R4 S7 t3 d, p

1 D1 x6 D6 g, j9 ?( G* V- O- T以上为"STDARG.H"的内容.! t7 j: W# u: S- G8 _: G
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;- t  ~" O- T+ T) h, E, G# ^
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,( M& ?6 G, M! x" x5 C
parmN为可变参数的前面一个固定参数.. ^  g- d0 L% Y
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
" O& `2 g' ], H+ r# Y& lva_end(ap) 结束可变参数获取.
% ~: p1 i" x5 O/ x; |' f( P
3 ]# x$ L) J( X0 J/ t/ V8 |* m' a+ H3)可变参数的使用实例
0 D5 V8 m- v9 h7 P* a: c! P1 B5 F$ [% m7 T; T" f* E  F
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串./ W! R$ ~: E$ T$ `! c9 `% l) n6 l" [1 M
# Q4 G/ s  R. J! `+ t
#include<stdio.h>* D$ d( H4 H5 }' R1 B* g  q
#include<conio.h>+ ^; O- r, ]6 q. k
#include<stdarg.h>
# c  z4 l: _. b' z  uvoid tVarArg(int num,...);/*num为可变参数的个数*/( v. a+ j$ |7 z2 Q
int main(void)
& [4 H% Z8 {7 E- I% S$ j{
  o" ~0 N) k/ m6 L  b/ iclrscr();) m- R; Z* j% s; R! A- _; u
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");" S- S$ V6 f! x- o9 j# J8 |) H, x3 T
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");$ o: C6 [  n$ W" _- f) [" m
getch();
1 G4 ?7 P6 T; I# z. k1 K' S+ lreturn 0;
2 M  T7 ]6 X( V}
- Z5 U& B+ y% |void tVarArg(int num,...)
" o5 O2 i' F# q{
- T# o4 [  i8 L4 Y; O8 pva_list argp;  /*定义一个指向可变参数的变量*/
" X% \/ H8 m6 F3 g/ ^5 Cva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/9 t" O# J( l5 k- a
while(--num>=0)
% a) L) E7 U3 y- r! S  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,$ O2 ?8 @' I+ @! C% Z
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/4 @8 @0 {& {. Y) a4 s
va_end(argp);  /*结束可变参数获取*/' u& J3 |5 v  T' e0 W
return ;
( y; N' [  \, c" e0 o( N* e}4 ]5 p0 {5 `6 \% h6 J1 L; [

) n% `" m1 u0 Q$ ^0 \5 T8 L" W4)可变参数的使用需要注意的问题
1 B( y3 G( _  G/ E/ R7 T: ]4 F+ z! Z
1.每个函数的可变参数至多有一个.
3 w4 C; M3 @0 \6 |1 \$ _+ g1 u/ O2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
# X+ E4 B- M3 x+ H3.可变参数的个数不确定,完全由程序约定.
9 }; @) `# d/ [- V6 b* v  U4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.0 r  P2 `5 x; M/ T$ o, O
而printf()中不是实现了识别参数吗?那是因为函数 ; R. g- ]  F- P2 J$ x* F* V
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
' W; v" L0 V6 K& p2 v6 n; n% s) o, L的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 + n( A+ v+ ]( T0 ^: N
过在自己的程序里作判断来实现的. & \; C& G+ K/ `5 n7 B3 D4 _8 q
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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