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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
7 ^  f0 c/ O, x6 h它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.& t7 i% s* Q( M1 r
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
: s, T+ S4 s5 B定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有* Z2 M# i9 \8 J" O% Z. k5 [" X
实际的名称与之相对应.
3 |+ M4 P0 r, X( L! F7 y由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
9 K. B! V5 M& ]! D, ]然而,更多地自由,同样也加大操作上的难度.; J. G5 c1 d0 D+ s( V5 e
以下就对可变参数的几个方面作一定的介绍.
5 \; U+ ~9 k2 n# J
% N! h$ Z) P4 q! C  Z8 Z1)可变参数的存储形式.
& b8 W# [2 l. a2 T5 W8 x/ T9 d5 |/ |6 S
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
% s" |( j! G' d1 Y' o存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
) j" K  _! K2 B" z' U在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,! @0 Z+ F) T: W  H' [# m9 H
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
  ^3 x$ }, h, a; V. j, Y. c- O2 s0 I因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):% A3 U* `4 G6 s7 y/ p$ K6 S
栈区:
/ W! N7 q# N. C% g4 C8 h% z5 ~7 O6 |' j( u# Y( u& y  g( R  g
|栈顶             低地址
4 e* c8 h+ F0 |6 \+ v9 {/ H" L
) h% h& g3 T2 X/ U+ P) v|第一个固定参数var15 c4 r0 S% X" Y7 S$ M& X) h
|可变参数前的第一个固定参数var2
) i5 j1 n" R. ?  p4 j4 _7 z|可变参数的第一个参数% C6 q9 B7 |0 v8 m- h
|...
- v! h2 J( }4 ]: t|可变参数的最后一个参数- h! M0 R6 O' v, I6 d0 G
|函数的倒数第二个固定参数var3
$ ?( S6 J* O) p" b" {/ u|函数的最后一个固定参数var43 n7 o5 [( Q6 \$ o6 Y
|...
9 m& P; p1 H9 B7 T3 ~; y8 t0 e" L|函数的返回地址
8 ?2 b1 Z: v, P2 q$ f! O% ?|...
' }1 B4 ~" k' m, y4 j|栈底    高地址# L: F: @3 j$ J
$ K( z5 s4 ], |+ T$ o6 M
2)使用可变参数所用到头文件和相关宏说明
3 {: o3 L7 r7 f: h# `
& C$ s, R2 B  x1 t3 ]在此,以TC2.0编译器为参考对象来说明.  w" \1 [% V+ t
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.
2 h# y5 T  ?7 y% G2 G5 @+ F此文件为:
) G& ?8 x! F0 ]5 z5 {$ y1 ~8 i/* stdarg.h# D9 N3 D- o3 g4 k

& s$ P  I0 H; D+ oDefinitions for ACCESSing parameters in functions that accept
4 a0 M$ j% ~; K; m# `0 V- a, ka variable number of arguments.
6 W4 S3 c4 @# T
/ H+ [" }1 b/ D1 T8 [/ Z! v/ YCopyright (c) Borland International 1987,1988
, F, v, Q% t6 D+ z6 h+ a& C+ oAll Rights Reserved.3 R$ p2 E/ i+ n/ v/ N# u
*/" |  g; s5 X# l$ w- k3 O
#if __STDC__, e1 L% ~* X! L6 e  m7 v9 ]7 L
#define _Cdecl
, O8 v: n/ \1 {#else2 g- Z2 ?6 z; [4 e' G
#define _Cdecl cdecl
, I5 M) ~* L4 M3 U, |#endif) K2 W* @' f2 X: Y  z! v- q: D# [
& Q3 Q! M& [0 l9 n( U! a
#if !defined(__STDARG)
& x, l) R* b4 o# e) Y9 b+ M#define __STDARG9 x# `- W* L8 D: q9 m1 J5 g
* C6 ?; C- n# ]9 r, p2 e5 |) h
typedef void *va_list;/ |3 \. h" t: a! T+ W0 z
, U0 v2 \9 N+ h( @
#define va_start(ap, parmN) (ap = ...)4 }5 F  A, g- Z5 D: W. c
#define va_arg(ap, type) (*((type *)(ap))++)
9 O/ C( t0 k# U( d( \2 M#define va_end(ap)
& w/ ^" j% |# Y1 f#define _va_ptr   (...)
, U: o8 H9 P* k' I: G#endif
1 ?. n' Q, j9 ?' W: c& Q
* Y4 y: v& }, p7 K. ?# e% v+ @以上为"STDARG.H"的内容.
; r% j1 V9 Y( `5 n7 J4 O$ S该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;9 k& p7 t* e* p) \3 }+ g+ W
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
% U, f9 \1 u- |8 D/ v0 sparmN为可变参数的前面一个固定参数.1 p& \! R/ l7 r5 F' l  @  x
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
& P2 y# O# f4 A) S8 Hva_end(ap) 结束可变参数获取.6 g6 k. t* ^' J2 l8 @

9 g7 M7 [) M+ H% s3)可变参数的使用实例9 R: K7 e8 q9 g) E( R2 [

0 n: ^" U, D# {, ~! Q: {实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.& B% B& M# K0 k# y4 I; x( [

% B4 ?* P( {, s+ v' i- z& w8 ~#include<stdio.h>9 U0 W9 ^+ q! ]0 g
#include<conio.h>3 `4 G2 x6 @+ \. \2 h+ A
#include<stdarg.h>4 C: k' O2 w8 U3 u7 M8 l5 _) ?
void tVarArg(int num,...);/*num为可变参数的个数*/% ~: A/ j3 r$ B' w
int main(void)8 B9 c0 n: C- _  M1 F- L
{
  s) N0 Z/ C7 }+ ?& G) qclrscr();
: U8 P; m& d( D% u( V/ }tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
; z4 N9 [$ o8 j% |; ]* |tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
& X, f6 _" a; p+ Y' l( xgetch();
$ y1 U0 ?2 I7 G# P0 N$ W2 treturn 0;6 l/ x9 x6 _) _  Y
}
/ i) S( x/ u* Zvoid tVarArg(int num,...)
6 R3 R+ I" \. x/ F$ [{
" u: z. l; T" B6 lva_list argp;  /*定义一个指向可变参数的变量*/5 Q% M% C  T% T' U# g  \9 v% u3 a
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
, [% u% P/ k. s6 Z  M( x3 Twhile(--num>=0)
9 |7 B! ?# a# p  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
" i" I" }$ o4 v- X/ k2 G' K9 C    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
* M: N. ~4 H& E2 s+ O7 hva_end(argp);  /*结束可变参数获取*/+ |% H. d" V6 v) @+ w
return ;- b! O" E% q& d1 {9 D
}" X* v8 J+ E: b4 _& B
4 b$ Y% B  V) U6 g; A0 J
4)可变参数的使用需要注意的问题
) c) F6 L0 H) g" n! o; j- Y
' y% [% s4 n# j- ?: }1.每个函数的可变参数至多有一个.
, z& s3 M) p5 X7 J) `2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.' B3 X7 |8 O2 a: F2 n! w
3.可变参数的个数不确定,完全由程序约定.; [  E( c7 ~2 }
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.8 k( V" l* {1 z) V
而printf()中不是实现了识别参数吗?那是因为函数 * V2 F0 O) [, m: D. ^& a" n
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg : _/ U3 Q( O' Y  K* i8 G+ K
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ' W: y' V2 L- U7 s9 r5 F) P: a" \
过在自己的程序里作判断来实现的.
+ _/ P& X5 c7 ?2 X( G! D5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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