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

函数的可变参数详谈

可变参数的英文表示为:variable argument.
2 ]- i  q6 R2 p- D; W它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.3 r+ d" T1 H$ Z9 a' b" Q5 w( i
可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不# x6 G6 X; H* L; G/ A
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
% l5 h1 t- M9 X& S' O: |& U实际的名称与之相对应.+ @" W' `# W" C; w2 }: y4 ?
由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.; T2 Q' Z7 U4 d! M3 P6 _8 f
然而,更多地自由,同样也加大操作上的难度.# Y5 \8 `7 p; N' `' B% Z! X
以下就对可变参数的几个方面作一定的介绍.! \( ~! c3 D& ^( o

- |/ s6 f; y" b8 L1)可变参数的存储形式.! B3 z1 |& r. `4 `3 j9 H- Y0 r# l

$ @  k  k! t8 Y. n大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,0 ?) v/ a, O  f; h" \: v
存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.
; [) N$ C4 q5 a7 |9 r8 Z: ]在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,
% P# Q( N! a+ g5 F+ U这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.# s; d) m2 C' a- h& J  e
因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):- K+ A* b' C4 g( I: [
栈区:
0 ~" d1 W9 h8 b! F$ o7 C3 [. w3 f; j; u2 n% q; t
|栈顶             低地址
# E# a9 E$ I6 p1 {. q0 S" G7 `- m$ s
+ T% n% k1 Z" a|第一个固定参数var1
; C' M2 F) o& d1 w8 \7 J|可变参数前的第一个固定参数var2! F0 @1 ~; @% J; I7 u* E, L
|可变参数的第一个参数
" r8 B, a( b( R) V8 N: c|...
) x/ I* T( g& E- B% L! p|可变参数的最后一个参数
9 f/ K( B: G2 y5 \$ r" `& z( Y4 b|函数的倒数第二个固定参数var3
8 o# _& f9 n6 u, S) A2 r|函数的最后一个固定参数var4" D; U7 k! x, Z5 q; p  w+ o$ h
|...
$ K3 ~9 K& w1 ]: P) x6 [9 c|函数的返回地址
( z5 H$ D6 j" w|...
+ o$ C! V0 j! s% P3 ^6 h  w|栈底    高地址! G- C/ z+ R& ^: q

* Z9 q6 S0 v4 B5 d* R, I) ^: p2)使用可变参数所用到头文件和相关宏说明* b5 o# U! w+ v) X" j
3 f5 U) N7 Q/ T
在此,以TC2.0编译器为参考对象来说明.
. B5 P9 c. G9 u6 H2 U3 D可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.: a! D) @7 b$ A3 E/ Y, O: x" U
此文件为:" q& A7 q* w7 U) C( N0 t+ v% M
/* stdarg.h
2 n  \5 q% x# C* l) Q, R6 B1 c  P9 t  B. V9 N# G6 W
Definitions for ACCESSing parameters in functions that accept
# @8 P0 e) r+ a. [' l0 H1 k! na variable number of arguments.
$ E& E8 H* D" \) o# n9 d9 L/ y# c9 ~5 \* `- P# i+ z0 o* h6 d
Copyright (c) Borland International 1987,1988
$ i( Q/ Q4 v2 `5 c' e1 Z4 n; YAll Rights Reserved.
  i: x4 Q2 b& e9 p5 L. }*/* B7 n! y! Y( t) ^% m3 l+ A3 G# E
#if __STDC__9 U" G2 k& z; g
#define _Cdecl
- i0 Z) o3 m3 g; t0 Z#else% i& ?* p6 y$ |! i6 ~5 y7 j
#define _Cdecl cdecl
% M# z* X3 N& o/ Y8 `#endif
" @) J6 Z- y- J0 V) _
. y4 G/ t/ a$ N8 V/ L0 z5 s# j' f#if !defined(__STDARG)
1 C; v1 S4 I7 O7 p6 Y$ ^#define __STDARG
% \; ~! @$ @# H* M. \+ u& J' l; v& D8 p' J2 Q" s
typedef void *va_list;
% H3 }$ {9 o1 n, I  ?. ?
" I3 ?* k+ F. ^/ M. c# m) E$ `) L#define va_start(ap, parmN) (ap = ...)1 q4 O! R/ e8 v& F- n1 T
#define va_arg(ap, type) (*((type *)(ap))++)
, R* k% f; y8 [& o" R; X( x#define va_end(ap)
) o7 v2 x1 u/ d#define _va_ptr   (...)
  J8 X3 w6 S- E7 |! {  z1 K/ j9 n6 e#endif
. @' d7 I8 D, W- w8 w; h! c) `
- {% S! L+ N$ b5 T! R5 C1 S- W- U以上为"STDARG.H"的内容.6 f4 ^$ C: i  I* ^
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;; D' {+ @, f2 I$ ^- N
va_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,
6 {1 t1 [2 }5 m" MparmN为可变参数的前面一个固定参数.4 T# v$ G1 l* j; v( h
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型., _2 L9 y2 i# q+ Q
va_end(ap) 结束可变参数获取.% O6 r" I. v- L: t) _6 Q% z7 R

. i. H/ ]: e. j6 e8 Z3)可变参数的使用实例/ n; m9 R, o1 M/ F4 {, i7 m

% r5 d7 b' n. U5 N实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.$ j- R' q" \5 s5 a/ N

/ j: G/ F2 k0 U  R#include<stdio.h>2 I. q0 }8 d9 q$ z4 m
#include<conio.h>1 K, T8 X" G8 x5 b
#include<stdarg.h>
1 _8 b" F5 ^* t; z8 l" ~8 t$ dvoid tVarArg(int num,...);/*num为可变参数的个数*/
& I( L) N. H! ]! Dint main(void)
# P1 y- K8 Y) K{) O# c" ?$ T& N% F0 E- |5 n
clrscr();$ S, ]: A- B* m" l, P6 W
tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");
7 m( M% @& X) C; ~- r0 ptVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");
" s" a8 s& Q" u5 U3 H/ L0 l: \  x- n4 ?getch();
: X. @5 ~" g. |% Vreturn 0;0 f" l- O5 N- \  I. L
}, n" `- A" e/ h, h, l, ~$ G
void tVarArg(int num,...)
; u7 E6 C; G2 Q{0 |8 V& ^& E- l5 o  v; N
va_list argp;  /*定义一个指向可变参数的变量*/& |6 i2 O+ H) B! c7 O, u/ D" i+ H
va_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
3 x% c5 y; t% twhile(--num>=0)4 Q  W) \" X! C5 c  N+ X
  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,
( }+ ^; B! ^( h& M1 F3 w5 K7 n8 S    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/( v3 p! A; ^' F! _8 F
va_end(argp);  /*结束可变参数获取*/
, O/ \3 O. \6 o# g  A0 \return ;
1 p' k& h6 g1 E# n}5 n5 I5 w/ j# b- R* A# s

& o0 a; d8 J' G4)可变参数的使用需要注意的问题! ?, C. t2 j, [) j$ N1 \. e4 L- Y
5 `6 J/ ]: ]* G6 i# M. G5 I: v, j
1.每个函数的可变参数至多有一个.
3 Z: C  d! q& \9 j$ I- s2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.3 Z; f9 `: d0 Z, L2 ?  J
3.可变参数的个数不确定,完全由程序约定.
) z" C0 n# ^1 j4 }9 i  @4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.6 g# q- q. d' o! V% V( N  W
而printf()中不是实现了识别参数吗?那是因为函数 & N3 }  m" l3 j6 }
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg " F8 P7 u- D# A$ N9 q
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 ; B6 S$ x1 O2 M3 e; I
过在自己的程序里作判断来实现的.
6 S6 `+ |1 O4 Z% [4 ?5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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