返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.' j0 Y0 t6 l9 N! B+ y' B0 ]" e/ X' ^
它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
; m. C5 ~* D1 N# y! ]9 m0 m6 S可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不
# G  ]0 G! Z$ t  i定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
  T/ O3 U- H* W: X4 ^/ b/ \) j实际的名称与之相对应.
) C. {, M9 X7 L, Q0 K2 X/ W由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.  v8 @. T5 \7 q' M" J1 E6 D  a
然而,更多地自由,同样也加大操作上的难度.% ]6 H, k. l6 t0 X* l6 _8 Z2 n& n- i( O
以下就对可变参数的几个方面作一定的介绍.
. ~9 o% n, i  X! q: W2 Y, x$ ]
" ]$ L- Y3 ^3 p  J1)可变参数的存储形式.- H  ]4 ?$ d+ O) y: H; m
" t: W- q$ |2 [: O4 x) Q' q
大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
% x% q+ j& s8 J" W5 S; W; m存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
/ M6 Z1 _; S$ A/ G* f% @- ^1 N在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
: Q- u) Z. m/ o3 a! _这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
; I" S% _& u5 }4 V/ Y因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):
0 a2 V8 i4 ?; P: [2 h! O, g% h栈区:
) E, B& ]$ Q# C/ O
  V7 n5 @& u0 c, _0 x( W3 }" R4 `|栈顶             低地址
2 r4 o7 ]; l6 B2 \9 ~' ?
' n' L( j# T. |' t|第一个固定参数var1
& i3 H) b( s/ t6 T( A6 f|可变参数前的第一个固定参数var2
1 w# [  r8 d$ I" ^1 Z# Q|可变参数的第一个参数
% M2 J  j' e- m' T. b0 J|...1 r: C( ^4 G; ?2 a  w
|可变参数的最后一个参数
, d9 O. o5 L& t0 @, m|函数的倒数第二个固定参数var34 \1 V$ {4 P3 ]: k$ [% L
|函数的最后一个固定参数var4  A* e* C! \" `& [9 n
|...  w, }3 N/ c5 e- a2 x' N
|函数的返回地址4 O3 p2 H9 w& D
|...
9 X5 z( @! J+ j9 ?; i|栈底    高地址
) R2 C) i: y0 E4 I4 y9 f9 x: b% K; z/ E4 k
2)使用可变参数所用到头文件和相关宏说明% E4 f4 [3 ?  [5 `% j) l2 e+ R

/ D) ^( f3 A" n, v# d& H' p在此,以TC2.0编译器为参考对象来说明.1 h! U! t7 o! t$ q6 |6 O
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.4 p$ }& a, Q& c
此文件为:% o6 K/ f  E+ f# W. C5 e( s2 K* d
/* stdarg.h9 O' K  i$ T1 H6 W! T; U( ^
7 t8 `) n' E6 X( o) C
Definitions for ACCESSing parameters in functions that accept
' t( y- y% }1 ea variable number of arguments.
% G' @" C" {" ]" c5 C1 f/ V9 b' p% |, H( X1 U& F
Copyright (c) Borland International 1987,1988; N/ P% }( ~/ T: P/ }; U
All Rights Reserved.
. W$ z1 Q+ G+ O! r*/* u4 `: ~3 t% ~9 }& U
#if __STDC__9 E. N1 O: l+ H. \/ w* ]; |
#define _Cdecl7 y& p9 N3 `$ V: W6 I* _3 D
#else# U5 p+ U/ g. t1 n+ T
#define _Cdecl cdecl
  n* F: N8 l1 s- x2 D#endif
2 D) o" i+ W" w, d
  W% ^& @; P  o#if !defined(__STDARG)
' i9 h* [: R( y6 `# `#define __STDARG
0 r' s) n/ x  H# m0 V: f7 B# g' R3 h1 l7 j
typedef void *va_list;
( v* O* n* o$ U
$ U) @% r" f; J2 n3 C5 d! b#define va_start(ap, parmN) (ap = ...). f' t  c; s; I& o5 C. R3 z
#define va_arg(ap, type) (*((type *)(ap))++)
% V% ]/ n3 z$ }  D' v4 j#define va_end(ap)/ c7 n5 a9 r- ^7 |( n9 {
#define _va_ptr   (...)9 ]5 y6 L+ G! n8 P& F5 Y
#endif( m1 L  W/ K2 {- M
! y5 V4 Q: X4 ]5 h$ e: e1 K
以上为"STDARG.H"的内容.
+ A' V# M/ _* x! j该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;  {* c% `6 v  d: v$ I1 q
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
; I& Q3 M0 \) `parmN为可变参数的前面一个固定参数.. n- o: z& J8 P. H0 Z$ K
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
8 a4 {' @* Z' _- u. [4 Wva_end(ap) 结束可变参数获取.7 d* N* H& N: K* I
  c4 @( U; p& P; |# f7 y
3)可变参数的使用实例
* @) u$ U9 F/ H- O( e& Y$ l7 I6 E2 D1 |
实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
2 j  {1 r7 `$ q5 z6 y0 h* D0 O  {: K1 A! f, s0 V( g  `2 g
#include<stdio.h>$ x  p3 H  n: W# i/ X9 U& N1 v+ c
#include<conio.h>
, E' @, G$ [4 N1 T( @' i  q#include<stdarg.h>
* {" N. p( e. ~3 c$ lvoid tVarArg(int num,...);/*num为可变参数的个数*/1 D2 i- u/ i! _7 i" ~
int main(void)
* s! h& H2 O" ]0 z4 r! T' {0 z% \{( J8 u/ v  g9 r5 ~
clrscr();& r6 \5 A4 J/ I; j
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
. @1 E4 X. M. {* FtVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
* u% ^' t/ p# j4 X# z+ [getch();
0 G( m3 N; r4 ~3 Wreturn 0;
/ t- w! J$ c+ g7 P  @! P% H  R}
+ Z# A# X/ ?5 ]: g$ @void tVarArg(int num,...)) X7 ^4 d0 \. C2 W9 c$ k
{
4 K4 j8 O3 r7 Y+ vva_list argp;  /*定义一个指向可变参数的变量*/7 h/ U$ L! ?) x& I0 ~
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
" B: S: X: D0 a$ W5 c6 d& {' Mwhile(--num>=0)7 E3 P, X4 I# L5 g; {9 Z
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,( Q7 Q2 a; `# ]9 I8 t
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/, t' e8 u2 m/ c
va_end(argp);  /*结束可变参数获取*/# W' Q" E/ {/ X/ ?8 i; e
return ;
* r/ e: A8 p& W) ~/ @}
. q$ k5 @  {7 |$ o
1 I8 l, B' @1 G5 |! z4)可变参数的使用需要注意的问题
9 X2 }  L2 `' N& d$ p- S' S! x8 k2 q" g# N/ H
1.每个函数的可变参数至多有一个.
( J1 ]3 }" F/ a/ N' N2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.8 q; [6 L$ w1 }
3.可变参数的个数不确定,完全由程序约定.
( w- N( m- z2 B) g9 X4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.
/ n" W) {# A: w2 j而printf()中不是实现了识别参数吗?那是因为函数 : I9 g- S: ]4 I) Q* d. v6 p% j
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
. E$ `- n# J* r9 c( q的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 / l# q7 i& g0 A- q% \9 `9 d/ n3 t
过在自己的程序里作判断来实现的.
5 U- \& F9 G) z' v. G/ `5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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