自己实现printf

int printf(char * fmt,…)

由此可见,printf为不定参数,难点在于如何获取后面参数。根据函数压栈顺序:
这里写图片描述
假设参数压栈顺序为从右到左,那arg_1为fmt,我们从参数可以得到fmt,那么为了得到其地址,只要&fmt就可以了,即我们可以通过&fmt得到arg_1的位置。得到了arg_1的位置,我们就可以通过&fmt+offset得到arg_2位置,然后依次就可以得到所有的参数了,但是问题是,offset是多少呢?这个就和arg_2类型有关了,加入arg_2是int,那么offset=4,如果arg_2是float,那么offset=8,
那么我们要怎么得到arg_2类型呢,别忘了,我们还有fmt,我们只要遍历fmt字符串,找到%,然后看一下%后面是什么,就知道arg_2类型是什么了。
为了获取参数,我们需要借助三个宏:

va_list
va_start
va_arg
va_end

三者在内核中定义为:

可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义 :

      typedef char * va_list;     // TC中定义为void*
      #define _INTSIZEOF(n)    ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统
      #define va_start(ap,v)    ( ap = (va_list)&v + _INTSIZEOF(v) )     //ap指向第一个变参的位置,即将第一个变参的地址赋予ap
      #define va_arg(ap,t)       ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   /*获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容*/
      #define va_end(ap) ( ap = (va_list)0 )   //清空va_list,即结束变参的获取

va_list ap ; 定义一个va_list变量ap
va_start(ap,v) ;执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即 ap指向第一个可变参数在堆栈的地址。
va_arg(ap,t) , ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。 ap+= sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型指针,这正是第一个可变参数在堆栈里的地址。然后 用取得这个地址的内容。
va_end(ap) ; 清空va_list ap。

最后附上自己实现的printf代码

void print(char*fmt,...){
    int ival;
    float fval;
    va_list ap;
    va_start(ap,fmt);
    for(char*p=fmt;*p!='\0';p++){
        if(*p!='%')
        {
            putchar(*p);
            continue;
        }
        switch (*++p){
            case 'd':
                ival=va_arg(ap,int);
                printf("%d",ival);
                break;
            case 'f':
                fval=va_arg(ap,double);
                printf("%f",fval);
                break;
            case 's':
                for (char* sval=va_arg(ap,char*);*sval!='\0';sval++) {
            putchar(*sval);
        }
                break;
            default:
                putchar(*p);
                break;
        }
    }
    va_end(ap);
}

另外,我们可以发散开来,
printf(“%d%d”,2) ; 按照上面的分析,会打印两个,第二个为随机值
printf(“hello”,2,3);只会打印hello

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值