返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.5 v& E! N) V% V3 K% y
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.8 v  Q0 U% m( ^% o
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不' c; W3 A4 x, h4 G, {
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有5 J9 Z; o* m8 z! H" R
实际的名称与之相对应.
; C% W' a7 v: [, U由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.9 @1 u7 a+ y+ ^8 x5 Z7 |
然而,更多地自由,同样也加大操作上的难度.4 w8 u) m' u* }* h8 K
以下就对可变参数的几个方面作一定的介绍.2 u2 [1 X9 d1 N- Y9 }. s  p8 t

0 s( X8 g: h* L1)可变参数的存储形式.
9 @$ V6 @4 l+ ^7 f  j" l2 ?$ d% b$ d; k" I; k8 r# p* N, f( o
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,% a$ I, o0 p( q6 t( N- M' E
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
/ c+ g3 Z' j7 Z$ j- ]/ f  f; ^在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
" I3 i# s1 a5 H  n4 ]. T这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# h/ v# \* T2 z1 B# m* u7 d. Z3 f
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
' s6 ]/ g1 m5 ^! n$ t( {3 R栈区:) o+ \% h2 H) i1 A; N9 B

' P3 ]6 m! r' B4 w|栈顶             低地址, i' s/ R" k: X6 }8 N5 b
5 H- A- g. Y8 n8 O; b
|第一个固定参数var1
2 Q. s; O  I/ f% J5 ~8 Y2 R|可变参数前的第一个固定参数var2
( [* `, G7 }# q. S& V5 G, i3 V|可变参数的第一个参数
$ g: y6 \$ I+ E7 T8 _+ l|...& r, q$ W4 r- `& {1 ^
|可变参数的最后一个参数& ^6 N$ \, R' ]. p9 z
|函数的倒数第二个固定参数var3
2 c" u2 B7 S5 ]  w2 x|函数的最后一个固定参数var4
' R# e) S  U) e+ s  M5 k% R" y( i|...
7 B( B( v. T4 s7 A6 A8 f* G|函数的返回地址
6 C. W/ }0 ]& Z  z* `6 M|...7 r4 A% Z9 c& t* h0 O) z3 K3 l7 ^
|栈底    高地址, Q/ W# P) L, y* ]1 j9 b, G/ C
  r! @2 U# t  b0 v% ^9 ^+ ^
2)使用可变参数所用到头文件和相关宏说明
, Y# _7 I2 N! f# A: n
1 R& E, S: ^  C5 |在此,以TC2.0编译器为参考对象来说明.' K' h% j1 Y5 c+ _) D3 |
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中., E% f, K3 b6 }1 n
此文件为:
5 k8 F, y1 s3 L7 ?/* stdarg.h9 T* k- V$ X$ s2 J  C

0 V3 v, {# f: l; I& j0 D, XDefinitions for ACCESSing parameters in functions that accept
4 n& b1 i# `+ O- a7 H3 Ha variable number of arguments.9 O3 ]3 ^5 x5 f" c/ W

& d  Y  T2 G2 D+ J& ^" E8 [/ xCopyright (c) Borland International 1987,1988
% Y6 F# A9 b# A, L* zAll Rights Reserved.
: f+ C& }1 L4 t& Z4 Z2 m6 X. w*/: N/ C* X4 R. N- u6 A% Q
#if __STDC__
: h" ?1 I' M3 b# X5 O7 J9 e#define _Cdecl
( t" i& X! N9 B#else2 I  g) G& B5 }; K
#define _Cdecl cdecl
1 B1 V( ~, {, D7 H0 `1 Z5 E9 {4 P#endif
4 y- P  }( e1 U- S* \  ^
4 |; U5 b: D% k) c#if !defined(__STDARG)
1 b' ]; ]4 y! _2 p1 g- l/ y#define __STDARG
- W# X: L+ d7 b) t# Y6 c( o% z7 p+ [6 |  E: t, P
typedef void *va_list;  r, o! P. i5 L" f% |" ^

# E: O7 u2 ~0 l* n* b5 ?#define va_start(ap, parmN) (ap = ...)3 Z3 K# t4 `6 G5 v
#define va_arg(ap, type) (*((type *)(ap))++)* f( }; ^: {. J0 A" r
#define va_end(ap)
; H3 r3 y- L+ O9 z#define _va_ptr   (...)" v! ~1 F4 R; t/ `" R. \/ d
#endif
1 J, `* B2 A! A% O# \" y3 v$ g7 ~( }7 [
以上为"STDARG.H"的内容.
2 k, K/ m8 P# K6 I该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;5 u# N2 T5 Y4 b: |1 @% n( _
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
" u. C1 N; U( S/ _parmN为可变参数的前面一个固定参数.. Z" m1 ^* r* N+ W6 d
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.9 }# L, o7 x  `+ @+ U
va_end(ap) 结束可变参数获取.
" S( I' N" T: J8 X+ w) E% f7 ~' T  F$ `
3)可变参数的使用实例
$ w! i6 w3 T; c7 j/ I/ s# n
* |, V, U5 @. R3 x1 z实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
* _% ]4 I& N! d/ P* r* Q5 @6 O7 n9 U
#include<stdio.h>1 W6 k+ B0 Z- Q/ k+ V. X1 O. y
#include<conio.h>$ ]- c! n  I( ~5 O$ B: s/ d
#include<stdarg.h>
" m7 W; ]: M  l: E* B0 \1 fvoid tVarArg(int num,...);/*num为可变参数的个数*/
! g3 W* v$ s# p7 oint main(void)* R6 K2 I1 a" ]
{8 P4 ~% ], J% x: E: x
clrscr();
, T1 d8 u0 n. E- N8 [8 atVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");# X: |% S1 }! @# y7 X
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
& ]# _) C% x( a' T, Y0 F! D$ t; r/ e8 Pgetch();4 N+ W- o1 o9 Q+ X* L
return 0;* B; ~' ]" w9 J
}
/ M: ~) ]. ]  f6 @% g2 x% C  uvoid tVarArg(int num,...)/ F1 ~( W/ d/ D# A" @
{7 U& v: _9 @3 D, z. t, L9 u. ?
va_list argp;  /*定义一个指向可变参数的变量*/
( P1 v6 X# M6 ?) N- Yva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/5 H8 w# C% p( d0 Q0 S
while(--num>=0)
- c5 O2 w* m- v, k  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
5 X0 v( m; }1 h    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/( W6 X: Z( S! l# ]/ t1 q; V+ Q
va_end(argp);  /*结束可变参数获取*/6 Q, Y4 j/ C% F" ?" `/ X, T+ ]
return ;& L% U' L6 x' q  D; ]
}
& N* K& n# y  v4 s* O' ~4 _' `3 Y7 F0 |
4)可变参数的使用需要注意的问题/ C8 E3 E2 Y: m7 X7 [

4 L* v/ O  S9 F1 V2 D* e1.每个函数的可变参数至多有一个.6 h; v6 x6 A( Y5 Z& o* J9 y; W
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
/ D4 o- s$ [+ F3.可变参数的个数不确定,完全由程序约定.
* ~/ v4 }3 ]" B; v2 A* u4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
# [# q. e8 V  h- I" I! S2 q% T而printf()中不是实现了识别参数吗?那是因为函数
. H: k) D' c9 ?1 H& W7 I4 W. w+ Vprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
* S) L' K9 @: D* t0 Y4 ^/ i& v的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
  j& u6 q9 d2 W. D( y3 L/ {( U过在自己的程序里作判断来实现的.
5 }0 Y+ d: k+ |( e7 \+ D! n5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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