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

函数的可变参数详谈

可变参数的英文表示为:variable argument.$ N9 `$ u& d8 Z$ O% [
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.9 g" ~6 W6 A: i4 j/ r$ L3 Y( X8 ?
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
& u' |& g- H4 }9 }定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有2 \8 P3 @" h( s6 {0 I% l1 s
实际的名称与之相对应.
" B) b$ O3 ^$ `/ [; f# }由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.
4 t: T7 n/ ~; S) Z: t; T然而,更多地自由,同样也加大操作上的难度.
+ o/ `8 M9 t4 w; U+ t8 u3 g以下就对可变参数的几个方面作一定的介绍.
0 U! U- d4 W# y& k# w0 \! |5 m
0 k; T$ ?0 M# k% j) m1)可变参数的存储形式.
2 @3 |2 I1 p8 `( q- n) z2 v: n6 E- o! |
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放," F& O3 w% _- g0 |
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
5 o8 q/ s/ ]( n& m5 ~$ I在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
6 d2 E7 c& S  ?. Y) e这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
& P4 a$ i" `' D' }0 f. r! D4 J1 a因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):7 n. U& m0 t! s3 f# T+ ^8 k
栈区:
. }! i/ q7 x# k6 D- G4 Q
5 Z3 p/ o, y. X" Y|栈顶             低地址
+ ]( Y  d1 J; R* d
7 {7 ?4 Q% c1 `. u* x" H. b) ?! h) ^, N|第一个固定参数var1  c! p5 p  ]+ u2 P: ^/ k& F
|可变参数前的第一个固定参数var2) ?5 [+ e7 h" s* e% M  k
|可变参数的第一个参数+ ^' [" t+ r8 a+ n. n3 i
|...
* b, W5 D. g4 Z|可变参数的最后一个参数# k/ ^: ^3 M  R
|函数的倒数第二个固定参数var3. [- G& H+ M- D0 `' w
|函数的最后一个固定参数var4% h, c+ K) s, Y( s, Y
|...% s0 b  c+ E6 c* E
|函数的返回地址$ Z6 l; Z7 h' l! C) Q% d# I; }
|...
% F6 w; F% W* J- T! ~1 Y4 ^3 h) ^( d|栈底    高地址
, i! H8 R. H: w5 V2 g$ q- W2 q+ W; J" ?
2)使用可变参数所用到头文件和相关宏说明& j* |4 E& v* ]  ~9 a
5 [* r$ D1 w% i* ~
在此,以TC2.0编译器为参考对象来说明.' G/ A5 U- `! {' Z, u9 H* j
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.1 \0 D- T3 q' T- Y. ~' ]2 K
此文件为:
7 r' `' A+ z* h8 Q# r/* stdarg.h& W" r" ?( ~" [2 B0 S1 ~
( T4 A! `9 t( U: X; S) x/ ^
Definitions for ACCESSing parameters in functions that accept
; u( S5 M9 U# G, qa variable number of arguments.
2 S. q3 b9 o$ i9 G$ Y' D/ Z, K; D: N$ [% _
Copyright (c) Borland International 1987,1988  P, O4 ?6 N+ a' n* l5 \
All Rights Reserved.
( i. \! j# i1 y  A4 }7 e*/
) g2 q9 ^, d: _. K: N; v4 |: O" {#if __STDC__
5 D# \9 j6 r5 x+ x' ?#define _Cdecl1 ~' e/ C7 Q3 ]- h0 K2 {" ~
#else$ x8 K9 g: C! ?, `
#define _Cdecl cdecl, Y3 U6 ~5 t: w9 I0 p2 j$ @
#endif9 V5 r$ m/ w1 Z& J3 \. h$ U( n# `
( r, L8 a0 t! }7 H5 V: t! I
#if !defined(__STDARG)
# Q+ w  U2 C. z1 @* ^#define __STDARG
) K5 l5 [+ F6 X
) J# Y- j" E. Q+ ptypedef void *va_list;
" h2 Q' r0 X: v4 C
4 d2 U7 D7 a$ F% P#define va_start(ap, parmN) (ap = ...)
# [3 L$ g2 q% C/ w% V#define va_arg(ap, type) (*((type *)(ap))++)
8 G+ ~% R2 ?, F* g6 T6 u#define va_end(ap)  M# o8 O# N* R
#define _va_ptr   (...)
& Z) ]; M" f# C  L' l6 A( h#endif1 R6 Q' s) W% n' {

8 g0 M" q8 ^3 `* T以上为"STDARG.H"的内容.+ N, N/ `" @$ c3 i5 I5 E3 [
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
% H5 T1 _0 i/ O& i2 p6 t8 d: ^* M: {va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,! d/ O+ W) {+ F5 p( @& z! I
parmN为可变参数的前面一个固定参数.
; l0 s4 X( ~( l+ a0 a# nva_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型." X' K5 q* U! h" P6 z
va_end(ap) 结束可变参数获取.* c  h& v) B, C
! s3 q& x& `! U$ w6 _
3)可变参数的使用实例
4 f+ J& y+ Q8 H8 O( B& n6 A7 C6 J2 p
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
% i& k$ z' }/ r9 r; p8 F
/ j" @% _/ ^9 z#include<stdio.h>8 T$ t* c. c+ Y0 `  c7 d
#include<conio.h>
$ V/ p+ X$ a  I1 z2 ^/ A#include<stdarg.h>
6 }$ V; s: O+ `& L( svoid tVarArg(int num,...);/*num为可变参数的个数*/
/ S2 ]6 I$ S, Y  Hint main(void): z- _* l9 O* v  u
{5 T4 u7 a$ M6 X* f( v  x
clrscr();
' x. {* z$ K7 @tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
' R6 X; p9 g' t0 T& YtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");- S9 i; L; G" x
getch();2 r2 Y" C% F! s& A; r2 k
return 0;
* @% {# Q: N- g/ S/ X& k2 t$ h4 @0 X}
, B$ _. Q$ O4 w, t! K0 B4 @void tVarArg(int num,...)9 _8 G$ x+ ]0 R8 i
{/ F9 a( g. o2 |3 r+ _
va_list argp;  /*定义一个指向可变参数的变量*/7 G2 ?; L8 ~# Z! C$ |, w6 Z! Y* s
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
8 O" T  ]" Z) F9 Pwhile(--num>=0)9 i0 E+ n, j7 \7 m# r
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数," Z3 U( z2 E& z7 S  u% {  M- `6 h- Y: t
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
9 _8 E+ X6 ^2 s, i: f- Q% {va_end(argp);  /*结束可变参数获取*/
1 v, w/ j8 y! Q2 d3 ]return ;$ V5 f/ j, g# ?' Q" O
}& I# a" ~7 T5 j+ W1 A. n

. z" |- N/ k; h0 r" ^! G" k. ?4)可变参数的使用需要注意的问题% }* J. V1 q$ s# F! U3 R

1 e# L, G1 ?" j% e1.每个函数的可变参数至多有一个.$ s4 G3 ]. o' j
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
1 z! B! j+ z$ X( F  Q0 B6 F3.可变参数的个数不确定,完全由程序约定.. c9 l/ v' N/ n9 i' k) L; S3 A
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
+ j; K( |0 T$ i" X' @7 A/ ]# v而printf()中不是实现了识别参数吗?那是因为函数
4 h* J6 M$ m5 W; D: Tprintf()是从固定参数format字符串来分析出参数的类型,再调用va_arg 4 W( v4 T8 |, M  D
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
3 t7 ~; }3 r) ~; m/ n% `6 K+ s过在自己的程序里作判断来实现的.
" H( _7 U# ]. c+ ^# P5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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