对于可变参数函数,自己一直以来都很关注,尤其当自己刚才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
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
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 (*p != ‘%’)
{//当前字符不是%号时,直接赋值,是%表示需要对输入进行转化
*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);
}
}
#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 (*p != ‘%’)
{//当前字符不是%号时,直接赋值,是%表示需要对输入进行转化
*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);
}
}
其他地方有对这个宏的定义为:
代码
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))
#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))