返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.) V; c2 U% |& n# j7 B3 H
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.% H9 d! a: Y! r- E. D
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
! S$ y+ B" v* B' R定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有" O: a. x& V$ |% w
实际的名称与之相对应.# n8 O7 t4 v( I! H
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.6 f& [: T1 t% s/ x% ?$ p! p
然而,更多地自由,同样也加大操作上的难度.% t0 H0 S; T  M! p& w1 F9 C
以下就对可变参数的几个方面作一定的介绍.! U4 ^! v) a, I) {

; c! O5 E) |' I$ u1)可变参数的存储形式.# q. N! U; h$ o. D" T

- s" S" L- V1 H' \3 @: ^大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
: B/ `9 y: J$ E% ~# j! O存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
5 n6 f: V3 B+ ?1 E6 G' F: F在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,$ `+ }7 n4 C3 j: T4 H" \
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存., n+ }+ _. J/ y5 e
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):2 r2 g3 _1 W7 S! }) V& |
栈区:
* h4 n# x( v6 y5 J- U! {
8 r" N5 V/ D% F1 A|栈顶             低地址
. ?5 \. d9 v) _4 O* N
! |  J  ?1 f4 ]& F8 s1 q6 y|第一个固定参数var11 P! F3 s& ~! s
|可变参数前的第一个固定参数var2
% j- n% H+ R6 ^% b|可变参数的第一个参数3 r1 D- F$ n! r2 l% p+ N
|...
3 ]# ~4 _  j; I6 k. Y$ X4 s& t|可变参数的最后一个参数
! J6 M: c$ p. n/ b  {8 E3 c5 U3 u! w|函数的倒数第二个固定参数var3
/ n2 [& _8 _& q0 M( m* e0 y|函数的最后一个固定参数var4
1 o3 j2 I, Q4 U9 ^|...0 u. F3 F0 U4 [
|函数的返回地址
9 \1 f3 [( P$ q' Q2 K& C; a! A|...
% _5 l3 ?2 T4 }- A8 Z|栈底    高地址
% x7 z2 z4 E/ ^! ^3 Q! r5 ~
' q+ l$ D" S4 B2 D$ J% f0 ]5 I2)使用可变参数所用到头文件和相关宏说明
. [! W3 ]4 ?) ?: V* o- s) W
3 D, |6 z# z' e( C8 J- T; W9 p在此,以TC2.0编译器为参考对象来说明.
. N* ~7 V; M8 t5 ~- z4 S2 }! _可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.' A! x$ G0 t" a2 h, P3 r
此文件为:
) g! a3 @. d" l9 O6 M" d/* stdarg.h% s+ G% q& V, V

( N# [# c! G5 ~' nDefinitions for ACCESSing parameters in functions that accept
* J: e) {/ U( x8 B6 La variable number of arguments.
# F& y+ ?+ ~: |7 U: q7 D$ C% E1 S& t7 c% I# [0 Y+ s) e$ Z
Copyright (c) Borland International 1987,1988
: C3 t; j& Q1 D, m9 k8 kAll Rights Reserved.
# {- p* N$ I+ h*/0 v' O  Z' `/ P1 g- A# }, c
#if __STDC__
1 ~6 f7 d; t) \, f2 f#define _Cdecl+ [' N( Y6 W' k: B: g  `! ?* A" [
#else5 s- z  l1 U% \( m- C1 Z
#define _Cdecl cdecl
: p" R, y) k; s3 S6 k: Z#endif
: p; g+ l8 {5 I/ a! w' z
0 {4 ]) D# [  C! ]) \#if !defined(__STDARG)
2 e5 [6 I- |- ~: A- }#define __STDARG
% R; c9 }, f; {; t8 J+ t% ?7 U$ I% M0 j; y. w
typedef void *va_list;& ~6 W' t5 p: A# a) T

' ~4 C# H4 P0 f7 A- P* K9 J" D#define va_start(ap, parmN) (ap = ...)
$ j) K5 G9 B% e2 B7 o#define va_arg(ap, type) (*((type *)(ap))++). L& |2 P+ C; A7 a6 G9 t
#define va_end(ap)6 n1 [7 e' L# e6 P
#define _va_ptr   (...)  c0 Y. r7 x( y0 @
#endif
5 L0 I4 G0 R9 _$ J  U& n4 C% d; b' A- l0 b2 A, ]/ N4 u" ]) P) F
以上为"STDARG.H"的内容.6 |+ W  B- v# \7 N: n2 R, F
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;1 t; O7 u4 b' V3 @: k
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,- G8 x5 P+ x. P- }/ W
parmN为可变参数的前面一个固定参数.
; Q: C2 N( i4 [9 @va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.3 l; c  r; v4 h7 M
va_end(ap) 结束可变参数获取.8 F9 @# e* A+ t  a7 p( S
* i* o/ V& z4 Z& i* X" {
3)可变参数的使用实例
1 M1 ?$ _) k! s( P% F# b
' ^# r7 q0 ?/ \& G( y7 V: {% O2 `实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
  q& Q1 k3 i) o$ T% l4 i
* r; R" h( {0 O2 v! Q#include<stdio.h>
* G0 b1 B7 Q0 W. M6 V#include<conio.h>2 }5 ^, }3 R5 N! F  f. J4 D
#include<stdarg.h>- b# j3 T# x& ~$ d
void tVarArg(int num,...);/*num为可变参数的个数*/; W9 j2 ?0 l; [1 ?
int main(void)
( I! Q. H0 H( }9 I( \) u{- B: i6 ~4 @2 f" m
clrscr();
5 i1 O0 N+ `5 ktVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
: R, y) Z- N0 y2 }0 W2 NtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");$ Z* k+ L6 i! c. b0 `/ l! A0 S9 u
getch();0 |) r! M1 q% |  s6 S
return 0;* r+ a# u) V8 U" f5 M! ^
}8 b# D/ s7 m( x$ |& B
void tVarArg(int num,...)
" j8 H! K+ b7 d/ |{0 r7 r" a2 b9 @4 N* Z* W
va_list argp;  /*定义一个指向可变参数的变量*/
- @2 W! K3 M- o0 L% s; d5 h( p2 e" Fva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/( N& u) A# r7 F* g9 I
while(--num>=0)4 i- F7 [) h2 e7 n1 q4 Y
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,) b9 Y7 a5 K' {/ n2 |5 m8 V
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
3 F, i- k6 Z6 q8 T  ~2 Gva_end(argp);  /*结束可变参数获取*// j, O' [: d  P: K' i( j4 P
return ;/ }# F2 b8 ~; |# ]5 b6 A
}
/ Z# @$ @0 g  g0 J6 m" s; _. w1 X6 I4 ^/ |6 n# c
4)可变参数的使用需要注意的问题
; ^, d, F6 @2 f: k) }" Y7 G
$ p- f: [2 F# `2 n, r1 U- S1.每个函数的可变参数至多有一个.( D# U# T; \+ s. o/ l2 c4 P
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数./ Z. S+ H& z+ @& N
3.可变参数的个数不确定,完全由程序约定.
  K$ @. @) R& \6 M4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.% K& A  b0 p! J( a: z, L
而printf()中不是实现了识别参数吗?那是因为函数 & c- Q. q+ I* K# l
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg , i/ t' W3 E1 Q8 o- L; [& ^
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
4 s7 m# E9 B+ i* l; _过在自己的程序里作判断来实现的.
( J6 [; Q+ F$ i3 K0 V- b1 W5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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