返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
' A7 `, |& E9 M+ _  B8 A它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.: \3 I. l& t6 m" r. a+ ]6 V" A
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不0 o% g1 M/ ~, P! ]
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
0 ~; f# A. u( @5 s实际的名称与之相对应.* S! U2 H5 @0 X2 F1 e$ M. ~' Z
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间." |& U3 O. C0 d. V
然而,更多地自由,同样也加大操作上的难度.
$ a  b; B- ~& f9 E以下就对可变参数的几个方面作一定的介绍.
* _/ A) n* f- U, p% q0 Z9 s6 d1 O) j5 Q, m4 J
1)可变参数的存储形式., p& v& ^( B3 [

0 [! L- T9 @5 N1 H+ o5 J  V大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,% ?3 d  A; {% h% Q
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.* U' x4 h( X( [- g
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,4 u# u2 u3 d; E4 ]4 F
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.% Y* ^  |+ C( z( C( Q) \- W! Q
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):1 C( N' \7 Y$ z: E8 b- a% ]! @
栈区:
: Q4 ~  a  M' e; e0 h0 G6 g" z" ]
|栈顶             低地址
; Q4 t! q' q' H: C% w- m2 y8 d3 _: P7 s& b  Y/ H9 x
|第一个固定参数var12 q+ @5 h* E8 I" r* a
|可变参数前的第一个固定参数var2- t* Q( C; ?, i
|可变参数的第一个参数
/ c0 T  u$ F1 d' m" k) @|...; r7 N/ h: ?2 X8 ^9 R( s
|可变参数的最后一个参数8 l; Y) M& L! Q0 `
|函数的倒数第二个固定参数var3
. h. y: r: X. K7 W/ w8 Z( a) a  o|函数的最后一个固定参数var4) h# w; \$ J& b. Z- m
|...2 d, X1 H. B. N) N/ \% {
|函数的返回地址- J5 d8 |# @" Z  s. l' [: U
|...6 k( v% G# I! o2 [
|栈底    高地址
3 d: G1 A! V2 l4 m  _4 H0 H
  c3 [1 D0 c( i. e' Z' q2)使用可变参数所用到头文件和相关宏说明: V/ x+ C) n- n- k+ t

3 W7 M% m" w( @5 R9 L在此,以TC2.0编译器为参考对象来说明.
  m6 x# D9 f' \7 ]3 K1 K可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.) q: |% Y3 H& g7 Y9 f) \. f2 j6 U
此文件为:
! N9 J" ^; C6 a' h/* stdarg.h
) v3 ]6 [* p! {6 Z/ j: A1 C0 X
, r, j3 \) |: _3 [! o: U) tDefinitions for ACCESSing parameters in functions that accept, |2 v' l  ^, U
a variable number of arguments.
6 Q+ X1 w' \9 u! l. Z7 C+ e4 b$ e0 H
Copyright (c) Borland International 1987,1988
6 K8 u& S1 @8 C. `3 b5 qAll Rights Reserved.
- E, e: O% _# r/ q4 H6 y*/
4 K5 [  n* ?' l6 y#if __STDC__, j$ X4 w7 b4 y  _$ c4 r5 e
#define _Cdecl0 Y" e6 k; a; @. y  V! @. _
#else
' s& E6 j0 C! x! ?1 ~0 {#define _Cdecl cdecl, x9 Q4 ~* v/ \; t! B
#endif$ X9 C0 l2 J* @2 e9 K

. y9 T. O& ^) V: B#if !defined(__STDARG): o2 G" m/ Y- T3 Q* u4 [0 S  O
#define __STDARG5 g# a* n5 |# {( D  x& v: f

  J/ [1 K7 E  z3 G, q# K+ Jtypedef void *va_list;, x+ \- Q( L- u6 ~" V/ d
5 E, p0 x9 H4 l* C" L, i7 k/ n
#define va_start(ap, parmN) (ap = ...)  a: }0 @! {  f/ b) K' D: k
#define va_arg(ap, type) (*((type *)(ap))++)/ D) X& _+ A9 |  {7 e
#define va_end(ap)
5 r$ A' a# t% i* G: X. V+ b" i% P#define _va_ptr   (...)% a' u: S  I6 Y" O7 I& G' r  I+ y
#endif" N* D% j" o; d( R: x7 `% t  b/ }
. t: |/ d/ F# Q( Q$ L( b5 _
以上为"STDARG.H"的内容.& U0 L1 `, K0 V
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
* g* i+ H6 A! E  b* a, O3 dva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,2 J2 W) }$ E! ], I& C' v" m5 w, A
parmN为可变参数的前面一个固定参数.
# ~" e, x- q3 kva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
  M' V, K: c1 |- cva_end(ap) 结束可变参数获取.
- H3 Q- Y8 ?$ y' v# T
: t9 Z, v0 d! o0 N: L2 M3)可变参数的使用实例) i6 N) T& r9 P8 q+ q* @! j
& L+ @$ M! |( ?! n) {  V# ~
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.- y0 K1 i/ ^% b
. H7 w. T& B$ m
#include<stdio.h>
, Z' V4 e) _3 B' c#include<conio.h>
/ {( i+ o5 f$ s+ e9 ^+ B* t. x* |#include<stdarg.h>3 ~  F: u, {: U3 G" B3 `
void tVarArg(int num,...);/*num为可变参数的个数*/
: J/ w  m2 Y$ Bint main(void)
, v8 X* |: A) K# r3 \5 w{& {) ^( ]4 A3 T) C
clrscr();5 i3 u! {0 D1 A7 k- V
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");- M6 D# e+ X) f/ U* s1 b5 y
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");: \2 U: z" a  _- G; A" s( ~9 @
getch();
* L1 ?. c( I/ V1 Q1 @( ^return 0;; e9 l: i2 ]" ~8 \- H* y
}. G2 d% j/ H5 U# n& S3 f) G5 O0 T
void tVarArg(int num,...)# W. c0 ^# }, f! W7 x! t, z
{
' h( S0 u4 V1 Qva_list argp;  /*定义一个指向可变参数的变量*/7 o# n6 r5 p4 @2 p/ Y, _
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
* m2 L, D5 M1 @1 J9 F" Xwhile(--num>=0)# K% R% S) `4 I3 Q3 Z( o# S( [
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数," y  {4 i, b3 e8 D7 S
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
5 G. W- \; `7 y5 t4 C$ Kva_end(argp);  /*结束可变参数获取*/
, o4 o8 `) {1 x' e: a4 Vreturn ;
# [' F4 H. X0 U7 b: m/ W}+ Y' l5 H$ I0 p- q
. r* z. c! \( m3 C3 e: a0 q
4)可变参数的使用需要注意的问题! t$ R& \6 o& @$ X! z8 `

& _" m% q3 a0 o8 ~; f9 X1.每个函数的可变参数至多有一个.. T  _. l) G- j  E7 {3 X  r
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.1 Y. Z6 {5 N1 K8 v' j* ^2 o) R) \
3.可变参数的个数不确定,完全由程序约定.
4 Y/ ?. x, [& y% W4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
, I1 k1 S' d5 ^: s) H而printf()中不是实现了识别参数吗?那是因为函数 / y1 n1 q5 P: `
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 6 J# m0 b' b6 J/ f
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 9 q8 k3 R- M2 k9 `" ~; V: ]
过在自己的程序里作判断来实现的. 2 k$ q% o- G$ X3 D. g9 m& q
5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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