printf可变形参的研究

本文详细解析了C语言中printf函数的工作原理,包括其如何处理可变形参、使用va_list获取参数列表,以及如何通过vsprintf函数进行格式化输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值