返回列表 发帖

函数的可变参数详谈

可变参数的英文表示为:variable argument.
. J" `; d  Z1 ?4 J它在函数的定义时,用三个点号'.'表示,用逗号与其它参数分隔.
8 c( C2 Q4 A. P5 r0 i可变参数的特点:不像固定参数那样一一对应,也不像固定参数有固定的参数类型和参数名称;可变参数中个数不! _4 R7 Y$ Y7 N" \5 l' {( E
定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有
8 H0 O1 s, h/ y4 A) }- U6 H实际的名称与之相对应.
1 R( J7 Z4 T" D由此可见,可变参数的形式非常自由而富有弹生.因些,它给那些天才程序员有更大地想象和发挥空间.# |: o5 P4 T8 ]# L& M" b6 a5 \
然而,更多地自由,同样也加大操作上的难度.
/ ?* j% w, ]/ j3 T# I以下就对可变参数的几个方面作一定的介绍.
5 ^; N0 k* y9 o2 \  b& N0 F( ]2 X/ y
9 W6 V6 p* O6 G( z& i* u( E1)可变参数的存储形式.* C6 c& {8 G! m/ z5 w

, }! u6 q, d4 m) C) x, ]大家都知道,一般函数的形参属于局部变量.而局部变量就是存储在内存的栈区(所谓的栈区:由编译器自动分配释放,
3 X& K4 }) u. N/ G存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。).可变参数也是存储在内存栈区.4 V3 p5 }) w+ e( g" F
在对函数的形参存储的时侯,编译器是从函数的形参的右边到左边逐一地压栈,5 e" @) A3 P/ h3 f$ t8 ~
这样保证了栈顶是函数的形参的第一个参数(从左到右数).而80x86平台下的内存分配顺序是从高地址内存到低地址内存.
9 z0 p3 e6 Z9 L3 @因此,函数的形参在内存的存储形式如下图(以fun(int var1,int var2,...,int var3,int var4)为例):# K" M5 }- k' W5 ^6 Z# h* e
栈区:
0 \5 T0 g; D8 f) A$ g
7 X; i! r& Z  h+ }|栈顶             低地址8 f6 L7 r+ c( a% u5 s; r) }

5 t* ]7 H! ]: k: y$ N7 y|第一个固定参数var14 q2 r+ s/ a; q
|可变参数前的第一个固定参数var2
2 v" m1 C- X' z" O) {|可变参数的第一个参数
1 W& c5 ?! q' |: b* f|..." i. N% V7 v* E$ F
|可变参数的最后一个参数
" f$ y# Q+ P! \$ F|函数的倒数第二个固定参数var3; D% Y7 l8 T: X$ G. S
|函数的最后一个固定参数var45 A( V0 R- _& G& r
|...7 ?" k! P0 U( K( V0 ~( [, ]+ o
|函数的返回地址
# o- I) Q* k/ a3 B/ S, D|...5 ?, m9 S+ y3 n) D6 u- i& q% ]5 z
|栈底    高地址5 x4 j  i  b; [. ?; ?" X4 t

: _# E5 }6 l" V) B# {2 I2)使用可变参数所用到头文件和相关宏说明, N2 w, e; I, Z0 E
" g  `5 l, Z; [: ~
在此,以TC2.0编译器为参考对象来说明.9 N" T( p& e: F( ^  w
可变参数的相关定义在TC2.0的名为"STDARG.H"的头文件中.* u: c' U7 d: W6 Z
此文件为:% A$ [  f$ X/ Q" ~! Z: L
/* stdarg.h
, Y" V, v) B' V# K1 `/ H) s4 J  J/ L2 F
Definitions for ACCESSing parameters in functions that accept9 a  r# v4 e3 Y
a variable number of arguments.
0 [) L2 @3 ^5 s& \" N5 u  B; o$ g3 O6 J# N1 }4 B
Copyright (c) Borland International 1987,1988/ D0 L  _4 [* V6 g1 U$ J" V
All Rights Reserved.
8 c; R+ _! }; U*/0 j( p5 @% d2 _  B
#if __STDC__& {. F# h% J* u2 ]! d  i; R3 |
#define _Cdecl/ y' ]& H5 c! w
#else
' j" a+ g' O. \% [. w6 f% o3 B#define _Cdecl cdecl
' c) o2 M. x4 k: @/ R5 E7 s' ?: H#endif
+ @$ q. x; S  {! L& q3 U# Q$ k5 ~( B6 W2 q, }8 `
#if !defined(__STDARG): n4 q- a! F9 t1 k, h8 e# f- G
#define __STDARG' V( ~4 E1 a& m, d
% M( ^( D3 {$ G) z3 T! P+ F1 E
typedef void *va_list;
6 y% W; A0 T, l2 Q% @& P2 Z2 T/ R" w! c" q3 J" R# k
#define va_start(ap, parmN) (ap = ...)6 }3 j+ X3 ~) S" q$ e
#define va_arg(ap, type) (*((type *)(ap))++)! [1 j  q3 l$ {: D, N
#define va_end(ap)
6 e& {# _1 W7 z& M. z#define _va_ptr   (...)
! f; @: V+ y" k3 {9 J#endif
2 W% _# e- b* I& |, |
* j- r0 N: ]: G$ e2 E9 e/ K8 ]; @7 `* n" w以上为"STDARG.H"的内容.4 [# i0 g3 n' N+ k
该文件定义了使用可变参数所用到的数据类型:typedef void  *va_list;
! D- q0 }' e  j/ y! `& Nva_start(ap,parmN)起到初始化,使用得ap指向可变参数的第一个参数.ap的类型为va_list,+ J4 x" c- G+ ~6 Y3 ?8 ~+ x7 W: o
parmN为可变参数的前面一个固定参数.1 E$ N4 v5 b8 c+ D' K- W
va_arg(ap,type)获得当前ap所指向的参数,并使ap指向可变参数的下一个参数,type为需要获得的参数的类型.
3 D& S) K- T2 r$ f/ w5 Bva_end(ap) 结束可变参数获取.9 N0 D) n3 q; V9 Z9 K" |9 |
$ k2 o7 c- J7 c2 s
3)可变参数的使用实例5 {- z, Q: G! J1 O

0 j% N2 }3 x, T  S5 v实例目的:用可变参数来实现个数不定的字符串的传递,并显示传递过来的字符串.
" I. o* u9 x$ M) E' v" w5 @- F. f. j' \9 S) N" l8 m  ^- i2 Y- G
#include<stdio.h>
( }; Y# D$ O3 ^9 k#include<conio.h>
' s3 r; _, Y$ y7 I#include<stdarg.h>/ Y* r! n9 u# f2 i
void tVarArg(int num,...);/*num为可变参数的个数*/
+ H: f" h1 i. Y, t$ Kint main(void)5 W- T5 C% b" X, _5 c$ x9 Z) B* b. T
{
- j6 s* f& r, y' Z1 E- nclrscr();
8 n: u+ K5 y# ~9 |tVarArg(5,"Hello! ","My ","name ","is ","neverTheSame.\n");1 z# E  }& p! O3 U2 q  K( T
tVarArg(8,"This ","is ","an ","example ","about ","variable-argument ","in ","funtion");2 ]( B( F/ \8 k" {& B
getch();# H# R  `' e- y) ?& ]) Y
return 0;
' N) j7 L! {' }: L0 b  _2 u2 ]}
5 E1 J6 ?7 v3 J9 M, e' w- V  V& _, S( lvoid tVarArg(int num,...)
  L- j. q! z" \{9 |+ l8 Q1 ~- m4 B( ?  Y
va_list argp;  /*定义一个指向可变参数的变量*/
5 C3 ~% P/ r% g% U& ~! P$ O, jva_start(argp,num); /*初始化,使用argp指向可变参数的第一个参数*/
  \( I! I& W& v- g9 u( U9 Qwhile(--num>=0)
+ l1 z* w3 U" x  Y2 y: j  printf("%s",(va_arg(argp,char*)));/*va_arg(argp,char*)获得argp所指向的参数,& u$ |3 K5 n9 z$ w
    并使用argp指向下一个参数,char*使用所获得的参数的类型转换为char*型.*/
, N0 c+ U0 ?9 X( d5 \& ^! \va_end(argp);  /*结束可变参数获取*/9 B& D( y2 R* L7 c
return ;  \7 L# V! W$ n6 e' V% m0 k' m
}2 c" z. P) z7 |2 M6 e

8 H# m  s1 K# g# M2 S, `4)可变参数的使用需要注意的问题4 K# b, n7 p! g/ |8 H# b
2 I* `! u7 q$ e- w
1.每个函数的可变参数至多有一个.# e  y  D* ~8 m5 B' D( m, @$ b
2.va_start(ap,parmN)中parmN为可变参数前的一个固定参数.
. N$ N+ h+ t) f; p7 e0 ^9 D3.可变参数的个数不确定,完全由程序约定./ u  x8 }' W0 q! ~! t( W
4.可变参数的类型不确定,完全由va_arg(ap,type)中的type指定,然后就把参数的类型强制转换.0 V+ ]8 @+ c2 u8 e$ x' Z. r
而printf()中不是实现了识别参数吗?那是因为函数 9 |  P( u9 ~  M: g) J5 i" i) K2 k
printf()是从固定参数format字符串来分析出参数的类型,再调用va_arg
. z& [4 ~  u1 M的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通 # Q* i& m: k; c8 ^- p
过在自己的程序里作判断来实现的.
' Y! r# [- n2 O: L0 V5.编译器对可变参数的函数的原型检查不够严格,对编程人员要求很高.

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