C语言printf函数的实现:
研究 printf的实现,首先来看看 printf函数的函数体
int printf (const char *fmt ,
...)
{
int i ;
char buf [256 ];
va_list arg =
(va_list)(( char*)(&fmt )
+ 4 );
i = vsprintf (buf, fmt, arg );
write(buf , i);
return i ;
}
代码位置: D:/~/funny /kernel/ printf.c
在形参列表里有这么一个 token:...
这个是可变形参的一种写法。
当传递参数的个数不确定时,就可以用这种方式来表示。
很显然,我们需要一种方法,来让函数体可以知道具体调用时参数的个数。
先来看 printf函数的内容:
这句:
va_list arg =
(va_list)(( char*)(&fmt )
+ 4 );
va_list的定义:
typedef char *va_list
这说明它是一个字符指针。
其中的: (char*)(& fmt)
+ 4) 表示的是 ...中的第一个参数。
如果不懂,我再慢慢的解释:
C语言中,参数压栈的方向是从右往左。
也就是说,当调用 printf函数的适合,先是最右边的参数入栈。
fmt是一个指针,这个指针指向第一个 const参数(const char *fmt )中的第一个元素。
fmt也是个变量,它的位置,是在栈上分配的,它也有地址。
对于一个 char *类型的变量,它入栈的是指针,而不是这个 char *型变量。
换句话说:
你sizeof( p)
(p 是一个指针,假设 p=&i ,i为任何类型的变量都可以)
得到的都是一个固定的值。(我的计算机中都是得到的)
当然,我还要补充的一点是:栈是从高地址向低地址方向增长的。
ok!
现在我想你该明白了:为什么说 (char*)(& fmt)
+ 4) 表示的是 ...中的第一个参数的地址。
下面我们来看看下一句:
i = vsprintf (buf, fmt, arg );
让我们来看看 vsprintf(buf , fmt, arg)是什么函数。
int vsprintf (char * buf, const char * fmt, va_list args)
{
char* p ;
char tmp [256 ];
va_list p_next_arg = args;
for (p =buf;* fmt;fmt ++)
{
if (*fmt != '%' )
{
*p++
= * fmt;
continue;
}
fmt++;
switch (*fmt )
{
case 'x':
itoa(tmp ,
*((int*) p_next_arg));
strcpy(p , tmp);
p_next_arg += 4;
p += strlen (tmp);
break;
case 's':
break;
default:
break;
}
}
return (p - buf);
}
本人模拟的示例:
/**
* \brief 模拟 Printf函数的可变形参类型的函数写法
*/
void MyPrintf( const char * fmt,...)
{
// 获取“”的第一个参数的地址
char* arg =
(char*)(& fmt)
+ 4;
// 获取格式化序列的地址
const char * pStart = fmt;
char* pStartArg = arg;
// 逐个获取格式化序列的内容
for (;
*pStart ; pStart++)
{
if (*pStart == '%' )
{
pStart ++;
char pCurSelect =
*pStart;
// 判断格式化的内容,然后输出
switch (pCurSelect )
{
case 'd':
{
int* pCurArg =
(int*) pStartArg;
pStartArg += 4;
cout <<
*pCurArg <<endl;
break;
}
case 'c':
{
char *pCurArg =
(char*) pStartArg;
pStartArg +=4;
cout <<
*pCurArg <<endl;
break;
}
default:
break;
}
}
}
}