返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
; K0 j8 r0 Q2 o0 \: m( O$ x它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.0 D: ]6 K6 l! e; \: r
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不* c0 s) t6 I& `6 u" K! ?$ X
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
! d* `9 w# `; H& O- A- Z& j实际的名称与之相对应.3 i2 W9 s$ b4 H8 B7 s$ y" n
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
' u+ O- r5 \+ G- E) D7 d然而,更多地自由,同样也加大操作上的难度.9 D7 e4 j. X: R# H! u% k' i
以下就对可变参数的几个方面作一定的介绍.- Y/ s: C1 G9 R7 D' Y, t7 Y: x- h
2 y! D% j" [! `$ Y: D
1)可变参数的存储形式.
- A* ?4 _2 R# ], d! Q
: p# f6 v. ^; e  B$ P4 Z, }大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
- g4 x6 L& K7 Q1 ?$ T, [" P存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
# y  g9 m, ~8 k& ?3 F在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,) \2 ?" }9 d8 P" ?: W
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.) K8 K' [& q8 a7 c6 D( ?; {. R# }
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):8 q- h) F# @  F( J/ I6 I$ g% f% `. O
栈区:
9 Q; y7 f8 X; h0 ?# ~! X$ w4 O7 Y. j5 i
|栈顶             低地址
7 }( }" g$ I6 O2 p5 s! Y; D/ x! d
% B  {0 Q8 W7 O7 O6 g|第一个固定参数var1
  ~) [) i: q$ k- e: W8 O+ d|可变参数前的第一个固定参数var2* ^3 t4 w$ x2 l- P
|可变参数的第一个参数4 C9 y3 c  f( v3 t
|...1 n1 B6 M; [. J8 {  u8 V
|可变参数的最后一个参数
/ Q& W3 n5 ?3 b! i|函数的倒数第二个固定参数var3
6 E2 A( \% f. r  G- y5 C9 F" u9 r|函数的最后一个固定参数var4
! u- m0 }+ F/ _1 J5 r/ R|...
8 P% V* F. k; s& ^" [7 z|函数的返回地址
0 ~% F& ~1 r! p* Z+ `  a  O% s|...
, X+ P, ^- o; v/ F|栈底    高地址
  b$ E9 E" B5 W% m" }- \, ~
  F' v. n0 O8 |" v2)使用可变参数所用到头文件和相关宏说明
8 v% O9 I* O. w7 ^1 Q
  m+ \" v7 w! x& V7 ~1 T9 u5 k在此,以TC2.0编译器为参考对象来说明.
9 z! R9 y1 Z' X" A可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.* N: g, C! j, N( {. }
此文件为:% W9 U1 F; m' S9 v- E# X
/* stdarg.h
! E. @  {! G6 x, t( s- H8 L2 @- T4 B$ ^3 a7 y# v( k! c
Definitions for ACCESSing parameters in functions that accept
4 t! \2 \5 r. _* k$ ma variable number of arguments.
' E$ F, J: d0 m. j3 w. f* O9 T& o+ m
Copyright (c) Borland International 1987,1988
( @% p! Z5 }( S' K8 c1 U- ~  b) Y  [All Rights Reserved.$ Y% l& r0 n7 |4 Y9 q
*/2 Q5 x1 C: G) h
#if __STDC__, z, ~9 q9 |& J/ u7 e
#define _Cdecl1 K5 C( Q! ^6 A0 B- v
#else) Q% g; K7 U% e1 E; V
#define _Cdecl cdecl
0 E  Y" @) d" [6 L+ E7 S#endif
/ o6 d3 j, o& Z  A9 k. n% a
2 Y- r2 |9 D3 p8 }$ g#if !defined(__STDARG)
# [& r( l# H* y5 e0 L#define __STDARG
2 q, n+ c( c& U* U2 E% V) t2 q3 H6 t$ }* S# z0 q7 e9 i6 l
typedef void *va_list;
9 t0 }/ @: [4 S) N7 f6 Z; \5 C9 ]* o! F. J, k6 Z4 O0 k- g
#define va_start(ap, parmN) (ap = ...); ?' _! \: D4 w& U( W& m
#define va_arg(ap, type) (*((type *)(ap))++)
; ?3 {/ h8 \; a" E#define va_end(ap)) W# E3 t' ]9 v: |( j
#define _va_ptr   (...)6 c* T( B; f6 n
#endif/ c7 _" k. L& l  l; t' O: M0 @5 l

4 x, A7 j# \9 r* I以上为"STDARG.H"的内容.
1 V! n3 m+ r6 _2 `该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
7 X8 V# o0 z+ F( w+ N9 Xva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
$ o# s+ X+ g/ Y% ?parmN为可变参数的前面一个固定参数.
+ `% P+ a: d* Gva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
' v) x# W2 P& m5 A0 V. tva_end(ap) 结束可变参数获取.
0 B4 D, v* z# a( L: l4 M$ ~1 K4 t# u0 E: J9 `8 |. B0 @
3)可变参数的使用实例5 o( V% P, o9 W+ Y' }
( {. @( n+ P( y( O9 F0 w. u7 t& M- C* u
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.4 h3 q2 L0 w: V. I
% p# K# p! [, Z, l2 ]
#include<stdio.h>
) w" `4 C& O. s2 o+ r0 k! Z# ~3 K#include<conio.h>
5 D  R3 L( U& S, A' D3 O#include<stdarg.h>- x! t% i% ~' B& V( v
void tVarArg(int num,...);/*num为可变参数的个数*/
3 `! o/ F" i2 g$ z4 Rint main(void)! M, s; ]. i$ C2 i
{0 A8 P  y- f. c  J
clrscr();
6 n& K8 k+ p0 Y6 \7 w- jtVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");. {; x$ [" X$ s6 X( D; o
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");8 N  F8 o: I  V3 I9 r
getch();& v$ r/ Z3 j$ q1 `
return 0;
- r0 `1 T0 Y  l7 I1 w( h, G}
. U! g6 P: ?5 Q8 l) H9 \8 I4 lvoid tVarArg(int num,...)( U8 F( v' f, w1 r
{
8 b4 a+ ]" I- W8 K8 Rva_list argp;  /*定义一个指向可变参数的变量*/
$ P$ Q- g: D% ]- C# Z% [va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
/ R9 e7 S9 k0 q+ q( I2 i' l  cwhile(--num>=0)
% E8 ~! t2 k7 \, k: l  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
( L0 O( Q. o0 }% i# `# I* {! \) T    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/; Z8 {8 w7 V! j2 G2 K6 j' P
va_end(argp);  /*结束可变参数获取*/
+ z  \7 D/ T; U" f) n* O0 X, Sreturn ;
. @) K, i! s4 F/ n}
% c( B4 S; s( ?3 R; P& ]$ ^# q) E+ q: K3 y
4)可变参数的使用需要注意的问题
" H: E5 Y; W: p3 {$ S' G' a6 H4 D- K" P8 u8 H; G
1.每个函数的可变参数至多有一个.
+ Y( ~0 [  z7 c9 ~& m, V2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
+ H; T) _3 M+ b# v+ p9 A# m3.可变参数的个数不确定,完全由程序约定.
7 i1 P. ?2 M) p6 N7 _  b% r4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换./ G, u5 p' X# p; f
而printf()中不是实现了识别参数吗?那是因为函数
# b' e$ S, W$ i, Y5 Nprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 0 A/ L: x; ^1 ]1 o' m
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
, k, o3 W- `; I! o过在自己的程序里作判断来实现的.
6 O4 r. M! `( k5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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