复撸C系列(2)-Printf

Printf

上一篇的“Helloworld”用到了大家最熟悉的printf,那这次我们就来了解一下它的原理,当然,最好的了解方式就是仿照printf自己写一个出来。

把”World”当做参数传递给printf:

printf(“Hello, %s\n”,”World”);

参照上篇,我们可以知道,前面的” Hello, %s\n” 是参数 char * fmt ,后面为省略参数。

思路:

第一个参数是字符指针,此刻正指向’H’,我们从它出发把前面的fmt扫描一遍完成解析。

解析过程:

1.      当遇到简单的字符以及换行符时,简单地写进最后的buffer里面就了。

2.      遇到”%s”的时候,我们开始解析,这时对应的是”World”这个常量字符串指针,把它扫一遍填进buffer就OK。

实现:

1.      扫描,那就需要一个循环,循环里每次向前走一步就OK,这个没问题。

2.      我需要知道”World”的地址,这时上篇说的va_start(args,fmt)让args指向fmt参变量所占空间+1的地址,也就是第二个参数的地址,可以拿到。再用va_arg(args,char*)我可以得到一个char *指针,也就是“World”的首地址,并且args移向下一个参数的首地址。

 

这里分析一下va_arg这个宏

#define _crt_va_arg(ap,t)    ( *(t *)( (ap += _INTSIZEOF(t)) -_INTSIZEOF(t) )  )

 

1.  (ap +=_INTSIZEOF(t))  => x  这里可以看见ap 移动了 t这么长的位置,假设现在就是x.

2.  ( x -_INTSIZEOF(t) )   => ap 这里没有往回走,但是得到的结果是刚才ap的位置。

3. (t *) (ap)  =>pt  把ap强制转换为t*的指针类型。

4.(*pt)    最后取值。

所以这个宏做了两件事情,一个是让ap移动了t所占大小的位置,另一个是返回ap的地址并强转为t*类型再取值。

 

我们写个函数来测试一下:

void TestVaMacro(int para1, double para2, char* para3)
{
  va_list args;
  va_start(args,para1);    //拿到第二个参数para2的地址
  double val2 =va_arg(args, double);  //args向前移动sizeof(double),指向下一个参数(第三个), 返回第二个参数的地址并取值
  char *val3 =va_arg(args, char*);   //args向前移动sizeof(char*),指向下一个参数(已经没了),返回第三个参数的地址并取值
  va_end(args);
}
 
int _tmain(int argc, _TCHAR* argv[])
{
  TestVaMacro(1,3.22, "the string");
  return 0;
}

这里如愿获得了分别对应的值

 


那这样我们就可以写一个简单版的printf了:

void minprintf(char *fmt,...)
{
  va_list args;
  va_start(args,fmt);
 
  while(isspace(*fmt)) //前面的空格不要
  {
         fmt++;
         continue;
  }
  while (*fmt)  //开始扫描
  {
         if(*fmt!= '%') //还没遇到’%’号,那就简单打印出来,跳到下一个字符
         {
                putchar(*fmt++);
                continue;
         }
         ++fmt;       //这里已经遇到,那就看’%’后面接的是什么
         if(isalnum(*fmt))   //遇到数字了,这里写的是几就填充几个空格
         {
                intlen = *fmt-'0';
                char*buf = new char[len+1];
                memset(buf,0, len+1);
                inti = 0;
                while(i < len)
                {
                       buf[i]= ' ';
                       i++;
                }
                printf("%s",buf);
                delete[]buf;
                buf= NULL;     //用完置空,好习惯
                fmt++;
         }
         switch(*fmt)    //看数字后面接的什么字母
         {
         case'd':
                {
                       intval = va_arg(args,int);  //是个整形
                       printf("%d",val);
                       break;
                }
         case'f':
                {
                       doubleval =  va_arg(args,double);   //是个double
                       printf("%f",val);
                       break;
                }
         case's':
                {
                       char*str = va_arg(args,char*);  //是个char *字符串
                    printf("%s",str);
                }
         }
         fmt++;
  }
  va_end(args);
}
 

虽然不完善,我们也已经实现了一个简单版本的printf,当然,相比于工业级的实现(上一篇微软的实现)肯定是差了很多的,但是相信通过这样,大家对它应该有了一个更好的理解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值