printf实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bjbz_cxy/article/details/88097406

printf是c语言下非常常用的一个函数,几乎每个程序员在一开始第一个学的函数应该就是printf吧!

今天来实现一个printf函数,虽然printf是我们常用的函数,但实现起来其实还是蛮考基础的!

下面一步一步带大家实现:

一. 编写一个入口函数并导入标准io头文件

#include<stdin.h>
int main(){

}

二. printf函数准备工作

前言:

printf函数的第一个参数是格式字符,printf通过分析格式字符来判断该打印怎样格式的数据,我们需要打印不同类型格式的话需要进行不同的操作。

所以我们需要申请很多函数用来打印不同数据类型,这里我们使用标准c的putchar函数实现,注意printf内部也是调用系统内核里的io函数,但是这里我们不是做内核开发所以不涉及,这里我们使用putchar函数来实现。

putchar函数每次只能打印一个字符,这里我们自己编写格式分析,然后通过分析来打印,这里会涉及到递归,整数到类型转换。

函数实现:

一.这里先实现int类型的打印

实现思路:

1.利用逆序递归逆序处理工作

2.利用字符编码差来将整数转换成字符

3.为了保证字符太大不会大于字符ascii码最大值用%取余运算来保证每次只处理一个整数

4.每次/10保证整数每次能递减一个数

//int
void PrintD(int Data){
        //判断Data是否小于等于0保证递归栈尾不溢出
       if(Data<=0){
         return;
        }
       //逆递归,逆序处理
      PrintD(Data/10);
       //先Data取余,123/10余3
      //然后在+’0’转换成字符的ascii码
      // 因为字符1的ascii码是49,而0的ascii码是48,比如Data是自然数1那么1%10=1,1+48=字符49(后面来解释一下自然数与ascii码的关系)
      putchar(Data%10+’0’);//整数转字符
     //注意由于是逆递归,字符打印会被逆序打印出来(如果不懂什么是逆递归我后面会写一篇博客介绍)
}

二.实现float类型的打印

实现思路:

1.先取出整数部分

2.在取出小数部分

3.在使用打印int方式来打印取出的数值

三. 实现double类型

实现思路:

和实现float一样,只是double精度是8位(看位编译器)改一下精度就好了

只需要复制上面的代码就好了!

四.实现char和char*

实现思路:

  这个就非常简单了,char只需要直接putchar就可以了,char*只需要判断是否遇到\0就好了!

//char
void PrintC(char C){
        putchar(C);
}
//char*
void PrintStr(const char* Str){
while(*Str!=‘\0’){
  putchar(Str++);
}
}
//*对char*解引用指向第一个char首地址所以取第一个字符然后对指针偏移递增即可(编译器会根据指针类型大小来递增不同的自然数,比如int递增4,char递增1)

四. 进制打印

实现思路:

定义一个字符:

123456789abcdef

然后通过表达式取制定进制权值的余即可(后面详细介绍这个表达式)

比如123

123%16=11 选择b

123/16=7

7%16=7选择7

也就是等于7b

这是c语言里的一个进制字符转换算法公式

//0x
void PrintX(unsigned long Num,int Base){
//判断递归是否小于0
if(Num<=0){
return;
}
//逆序递归
PrintX(Num/Base,Base);
//取权值余,这是自然数转进制字符的一种表达式
putchar("0123456789abcdef"[Num%Base]);
}

六. 实现Printf
实现思路:
通过判断字符来分析格式字符!
 

//Printf
int My_Printf(char* ForMat,...){
int num = 0;    //打印字符数量
//可变参数列表
va_list va_l;
va_start(va_l,ForMat);
//获取首字符
char Line = *ForMat;
while(Line!='\0'){//终结字符
 if(Line == '%'){//判断是否遇到格式符号
Line = *ForMat++;//遇到的话递增判断后面的字符是什么
switch(Line){    //校验
case 'c':    //char
PrintC(va_arg(va_l,char));    //传递参数
 break;
case 's'://str
PrintStr(va_arg(va_l,char*));
break;
case 'f'://float
PrintF(va_arg(va_l,float));
break;
case 'lf'://double
PrintLF(va_arg(va_l,double));
break;
case 'p'://地址ptr,地址是多种类型的,所以使用void全能类型(c语言到时候会根据数据类型编号隐转换)
PrintP(va_arg(va_l,void*));
break;
case 'o'://打印八进制
PrintX(va_arg(va_l,int),8);
break;
case 'ox'://打印十六进制
PrintX(va_arg(va_l,int),16);
break;
default://如果不是任何一个格式,则直接打印
putchar('%');
putchar(Line);
break;
}
}
putchar(Line);
Line = *ForMat++;
++num;
}
return num;
}

完整代码:

#include<stdin.h>
#include<stdarg.h>
//int
void PrintD(int Data){
        //判断Data是否小于等于0保证递归栈尾不溢出
       if(Data<=0){
         return;
        }
       //逆递归,逆序处理
      PrintD(Data/10);
       //先Data取余,123/10余3
      //然后在+’0’转换成字符的ascii码
      // 因为字符1的ascii码是49,而0的ascii码是48,比如Data是自然数1那么1%10=1,1+48=字符49(后面来解释一下自然数与ascii码的关系)
      putchar(Data%10+’0’);//整数转字符
     //注意由于是逆递归,字符打印会被逆序打印出来(如果不懂什么是逆递归我后面会写一篇博客介绍)
}
//float
void PrintF(float Data){
       //取出整数部分
        int I_Data = (int)Data;
       //float精度是6位(这里说的是32位编译器),首先先将整数部分去掉
        Data -= I_Data;
       //取精度
       int Flo = 1000000*Data;
       //打印整数
      PrintD(I_Data);
      //分割符
     putchar(‘.’);
     //小数部分
     PrintD(Flo);
     //其实思路很简单,就是取出小数变成整数然后打印就行了!
}
//double
void PrintLF(float Data){
       //取出整数部分
        int I_Data = (int)Data;
       //float精度是6位(这里说的是32位编译器),首先先将整数部分去掉
        Data -= I_Data;
       //取精度
       int Flo = 100000000*Data;
       //打印整数
      PrintD(I_Data);
      //分割符
     putchar(‘.’);
     //小数部分
     PrintD(Flo);
     //其实思路很简单,就是取出小数变成整数然后打印就行了!
}
//char
void PrintC(char C){
        putchar(C);
}
//char*
void PrintStr(const char* Str){
while(*Str!=‘\0’){
  putchar(Str++);
}
}
//0x
void PrintX(unsigned long Num,int Base){
//判断递归是否小于0
if(Num<=0){
return;
}
//逆序递归
PrintX(Num/Base,Base);
//取权值余,这是自然数转进制字符的一种表达式
putchar("0123456789abcdef"[Num%Base]);
}
void PrintP(unsigned long Num){
if(Num<=0){
return;
}
PrintX(Num,16);
}
//Printf
int My_Printf(char* ForMat,...){
int num = 0;    //打印字符数量
//可变参数列表
va_list va_l;
va_start(va_l,ForMat);
//获取首字符
char Line = *ForMat;
while(Line!='\0'){//终结字符
 if(Line == '%'){//判断是否遇到格式符号
Line = *ForMat++;//遇到的话递增判断后面的字符是什么
switch(Line){    //校验
case 'c':    //char
PrintC(va_arg(va_l,char));    //传递参数
 break;
case 's'://str
PrintStr(va_arg(va_l,char*));
break;
case 'f'://float
PrintF(va_arg(va_l,float));
break;
case 'lf'://double
PrintLF(va_arg(va_l,double));
break;
case 'p'://地址ptr,地址是多种类型的,所以使用void全能类型(c语言到时候会根据数据类型编号隐转换)
PrintP(va_arg(va_l,void*));
break;
case 'o'://打印八进制
PrintX(va_arg(va_l,int),8);
break;
case 'ox'://打印十六进制
PrintX(va_arg(va_l,int),16);
break;
default://如果不是任何一个格式,则直接打印
putchar('%');
putchar(Line);
break;
}
}
putchar(Line);
Line = *ForMat++;
++num;
}
return num;
}

int main(){
    My_Printf("%d%c%s%f%lf",1,'d',"hello word",0.12,0.34567);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bjbz_cxy/article/details/88097406