MTK优美代码赏析8:可变参数的C函数

对于可变参数函数,自己一直以来都很关注,尤其当自己刚才C#上转到C时,一度曾研究C上的面向对象,将类似printf的函数归类为面向对象里的函数多态,当时只是一个主观感受。今日仔细看了下代码的实现,发现使用C提供的公共接口是可以实现的,但这些接口是如何实现的就不知了。。。

文件:stdarg.h

gcc中的定义为:

/*  Define the standard macros for the user,
if this invocation was from the user program.  
*/
/*  Define the standard macros for the user,   if this invocation was from the user program.   */

#define  va_start(v,l) __builtin_va_start(v,l)

#define  va_end(v) __builtin_va_end(v)

#define  va_arg(v,l) __builtin_va_arg(v,l)

#if  !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L

#define  va_copy(d,s) __builtin_va_copy(d,s)

#endif

 

 

 下面看dbg_print(char *fmt,…)函数实现,部分内容摘自:《用库函数stdarg.h实现函数参数的可变》http://blog.sina.com.cn/s/blog_4ce3d23e0100efm3.html

ExpandedBlockStart.gif dbg_print代码
#define  MAXCHARS 512

#define  MAXFRACT     10000

#define  NumFract        4

static   void  itoa( char   ** buf,  int  i,  int   base );

void  itof( char   ** buf,  int  i);

char  print_buf[MAXCHARS];
void  dbg_print( char   * fmt,…)

{

#if  (!defined(IC_MODULE_TEST))

va_list ap;
// 定义一个指向个数可变的参数列表指针;

double  dval;

int  ival;

char   * p,  * sval;

char   * bp, cval;

int  fract;

unsigned 
short  len;

 bp
=  print_buf;

 
* bp =   0 ;

 

 va_start (ap, fmt);
// 使参数列表指针ap指向函数参数列表中的第一个可选参数,

/*

说明:fmt是位于第一个可选参数之前的固定参数,(或者说,最后一个固定参数;…之前的一个参数),函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。如果有一va函数的声明是void va_test(char a, char b, char c, …),则它的固定参数依次是a,b,c,最后一个固定参数fmt为c,因此就是va_start(ap, c)。

*/

 
for  (p =  fmt;  * p; p ++ ) // 逐个字符处理输入的fmt字符串参数

 {

 
if  ( * !=  ‘ % ’)

 {
// 当前字符不是%号时,直接赋值,是%表示需要对输入进行转化

 
* bp ++=   * p;

 
continue ;

 }

 
switch  ( *++ p) {

 
case  ‘d’: // 十进制数

 ival
=  va_arg(ap,  int ); // 返回参数列表中指针ap所指的参数,返回类型为int,并使指针ap指向参数列表中下一个参数。

 
if  (ival  <   0 ){

 
* bp ++=  ‘ - ’;

  ival
=   - ival;

 }

 itoa (
& bp, ival,  10 );

 
break ;

 

 
case  ‘o’: // 八进制

 ival
=  va_arg(ap,  int );

 
if  (ival  <   0 ){

 
* bp ++=  ‘ - ’;

  ival
=   - ival;

 }

 
* bp ++=  ’ 0 ′;

 itoa (
& bp, ival,  8 );

 
break ;

 

 
case  ‘x’: // 16进制

 ival
=  va_arg(ap,  int );

 
if  (ival  <   0 ){

  
* bp ++=  ‘ - ’;

  ival
=   - ival;

 }

 
* bp ++=  ’ 0 ′;

 
* bp ++=  ‘x’;

 itoa (
& bp, ival,  16 );

 
break ;

 

 
case  ‘c’: // 字符

 cval
=  va_arg(ap,  int );

 
* bp ++=  cval;

 
break ;

 

 
case  ‘f’: // 浮点数,这个处理比较特殊

 dval
=  va_arg(ap,  double );

 
if  (dval  <   0 ){

 
* bp ++=  ‘ - ’;

 dval
=   - dval;

 }

 
if  (dval  >=   1.0 ) // 大于1.0时

 itoa (
& bp, ( int )dval,  10 ); // 强制转化为整数后按十进制取小数点前的值

   
else // 否则直接输出0

 
* bp ++=  ’ 0 ′;

 
* bp ++=  ‘.’; // 设置小数点字符

 fract
=  ( int )((dval -  ( double )( int )dval) * ( double )(MAXFRACT)); // 将.后的值转化为整数

 itof(
& bp, fract); // 将这个整数转化为字符

 
break ;

 

 
case  ‘s’: // string

 
for  (sval  =  va_arg(ap,  char   * ) ;  * sval ; sval ++  )

  
* bp ++=   * sval;

 
break ;

 }

 }

// 补充一个: va_copy(dest, src):dest,src的类型都是va_list,va_copy()用于复制参数列表指针,将dest初始化为src。

 
* bp =   0 ;

 len 
=  (unsigned  short )(bp – print_buf);

 
// #if 1  将数据写入串口

 #ifdef __DMA_UART_VIRTUAL_FIFO__

 
for  (bp =  print_buf;  * bp; bp ++ )

 {

 PutUARTByte(DBG_PRINT_PORT,
* bp);

 }

 
#else

 BMT_PutBytes(DBG_PRINT_PORT,(kal_uint8 
* )print_buf,len);

 
#endif

 va_end (ap);
// 清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_ptr被置无效后,可以通过调用 va_start()、va_copy()恢复ap。每次调用va_start() / va_copy()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内。

#endif

}

// 功能:把10进制数字转换为数字字符串。

void  itof( char   ** buf,  int  i)

{

 
char   * s;

#define  LEN 20

 
int  rem, j;

 
static   char  rev[LEN + 1 ];

 rev[LEN] 
=   0 ;

 s 
=   & rev[LEN]; // s指向rev末尾

 
for  (j =   0  ; j  <  NumFract ; j ++ )

 {

 rem 
=  i  %   10 ; // 和下面i/=10配合,依次取个位,十位,百位,千位

 
* –s  =  rem  +  ’ 0 ′; // 加上字符0,将数字变为数字字符

 i 
/=   10 ;

 }

 
while  ( * s)

 {

 (
* buf)[ 0 =   * s ++ ; // unicode 转 assic

 
++ ( * buf);

 }

}

// 功能:把一数字转换为数字字符串,base表示type, 8 10 16 32等

static   void  itoa( char   ** buf,  int  i,  int   base )

{

 
char   * s;

#define  LEN 20

 
int  rem;

 
static   char  rev[LEN + 1 ];

 rev[LEN] 
=   0 ;

 
if  (i  ==   0 )

 {

 (
* buf)[ 0 =  ’ 0 ′;

 
++ ( * buf);

 
return ;

 }

 s 
=   & rev[LEN];

 
while  (i)

 {

 rem 
=  i  %   base ;

 
if  (rem  <   10 )

 
* –s  =  rem  +  ’ 0 ′;

 
else   if  ( base   ==   16 )

 
* –s  =  “abcdef”[rem  -   10 ];

 i 
/=   base ;

 }

 
while  ( * s)

 {

 (
* buf)[ 0 =   * s ++ ;

 
++ ( * buf);

 }

}

 

 

 其他地方有对这个宏的定义为:

ExpandedBlockStart.gif 代码
typedef  int   * va_list[ 1 ];

#define  va_start(ap, parmN) (void)(*(ap) = __va_start(parmN))

#define  va_arg(ap, type) __va_arg(*(ap), type)

#define  va_copy(dest, src) ((void)(*(dest) = *(src)))

#define  va_end(ap) ((void)(*(ap) = 0))

#undef  tolower

#undef  isdigit

#define  isdigit(c) (((c) >= ’0′) && ((c) <= ’9′))

#define  tolower(c) (((c)>=’A’ && (c)<=’Z')?((c)+0×20):(c))

 

 

 

posted on 2010-10-27 22:22  Anpher Zhang 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/zhangsufeng/archive/2010/10/27/1862999.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值