当前位置:网站首页>自己实现printf函数
自己实现printf函数
2020-11-09 15:25:00 【shzwork】
origin:https://www.e-learn.cn/content/qita/716071
首先我们先来了解一下基础知识。
printf格式字符如下所示,
| 格式字符 |
说明 |
| d |
以带符号的十进制形式输出整数(整数不输出符号) |
| u |
以无符号十进制形式输出整数 |
| x |
以十六进制无符号形式输出整数(不输出前导符0x), 用x则输出十六进制数的a~f时以小写形式输出 |
| c |
以字符形式输出,只输出一个字符 |
| s |
输出字符串 |
在C语言中,不仅参数的类型可变,而且参数的个数也是可变的.也就是说,在形参表中可以不明确指定传递参数的个数和类型,以上所说的printf函数就是如此.这种函数称之为变参函数。可变长参数函数的参数数目和类型虽然是可变,但其设计原理与固定参数函数的设计原理是一致的,我们有办法告诉变参函数没有指定的参数的个数和类型。
printf的声明如下:
int printf(const char *format, ...);
format:固定参数
... :可变参数(变参)

在C语言中,变参函数的声明是放在atdarg.h标准库中的,当然可以直接包含进来使用它,但我们这里自己定义它(参照头文件的宏定义)。
typedef char * va_list;
/* 当sizeof(n)=1/2/4时,_INTSIZEOF(n)=4 */
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//#define va_arg(ap,t) (ap = ap + _INTSIZEOF(t), *(t *)(ap - _INTSIZEOF(t)))
/* 下面这个话的意思是,先指向下一个变量的地址,然后再(减)回来,最后取它原来地址里面的值 */
#define va_arg(ap,t) (*(t *)(ap = ap + _INTSIZEOF(t), ap - _INTSIZEOF(t)))
/*指针用完后指向0地址,防止野指针的出现*/
#define va_end(ap) ( ap = (va_list)0 )
这里需要注意一下#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) -1) & ~(sizeof(int) - 1) ),可能不是很好理解,下面补充一个知识点:由于在x86(32位机器)平台下,GCC编译器默认按4字节对齐,所以当sizeof(n)=1/2/4时,_INTSIZEOF(n)=4这句话的意思是当变量类型是char,unsigned int,int,那么不足4字节的都按照4字节补齐。
下面给出My_printf.c
#include "my_printf.h"
//==================================================================================================
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_arg(ap,t) ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 ) //这里就不解释了,不难理解
//==================================================================================================
unsigned char hex_tab[]={'0','1','2','3','4','5','6','7',\
'8','9','a','b','c','d','e','f'}; //输出各种进制下的字符
static int outc(int c)
{
__out_putchar(c); //这里的_out_putchar其实就是putchar,在.h中定义
return 0;
}
static int outs (const char *s) //输出字符串
{
while (*s != '\0')
__out_putchar(*s++);
return 0;
}
static int out_num(long n, int base,char lead,int maxwidth)
{
unsigned long m=0;
char buf[MAX_NUMBER_BYTES], *s = buf + sizeof(buf); // sizeof算结束符'\0' ,strlen不算
int count=0,i=0; //注意这里s指向buf的末端,至于为什么继续往下看
*--s = '\0'; //先--,在赋值结束符,因为sizeof算结束符在内的长度
if (n < 0){
m = -n; //如果是输出的是负数就取反
}
else{
m = n;
}
do{
*--s = hex_tab[m%base];
count++;
}while ((m /= base) != 0); //将要打印的数字从个位开始一位一位存储在数组buf中,如果上面不是指向buf末端,
if (n < 0)
*--s = '-'; //负数的话加负号
return outs(s);
}
/*reference : int vprintf(const char *format, va_list ap); */
static int my_vprintf(const char *fmt, va_list ap)
{
char lead=' ';
int maxwidth=0;
for(; *fmt != '\0'; fmt++)
{
if (*fmt != '%') { //顺序查找判断,遇到%就推出,否则继续循环输出
outc(*fmt);
continue;
}
fmt++;
if(*fmt == '0'){ //遇到‘0’说明前导码是0
lead = '0';
fmt++;
}
while(*fmt >= '0' && *fmt <= '9'){ //紧接着的数字是长度,算出指定长度
maxwidth *=10;
maxwidth += (*fmt - '0');
fmt++;
}
switch (*fmt) { //判断格式输出
case 'd': out_num(va_arg(ap, int), 10,lead,maxwidth); break;
case 'o': out_num(va_arg(ap, unsigned int), 8,lead,maxwidth); break;
case 'u': out_num(va_arg(ap, unsigned int), 10,lead,maxwidth); break;
case 'x': out_num(va_arg(ap, unsigned int), 16,lead,maxwidth); break;
case 'c': outc(va_arg(ap, int )); break;
case 's': outs(va_arg(ap, char *)); break;
default:
outc(*fmt);
break;
}
}
return 0;
}
//reference : int printf(const char *format, ...);
int printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
my_vprintf(fmt, ap);
va_end(ap);
return 0;
}
int my_printf_test(void)
{
printf("My_printf test\n\r") ;
printf("test char =%c,%c\n\r", 'A','a') ;
printf("test decimal number =%d\n\r", 123456) ;
printf("test decimal number =%d\n\r", -123456) ;
printf("test hex number =0x%x\n\r", 0x55aa55aa) ;
printf("test string =%s\n\r", "yoyoyo") ;
printf("num=%08d\n\r", 12345);
printf("num=%8d\n\r", 12345);
printf("num=0x%08x\n\r", 0x12345);
printf("num=0x%8x\n\r", 0x12345);
printf("num=0x%02x\n\r", 0x1);
printf("num=0x%2x\n\r", 0x1);
printf("num=%05d\n\r", 0x1);
printf("num=%5d\n\r", 0x1);
return 0;
}
实验结果如下:

版权声明
本文为[shzwork]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4000302/blog/4710035
边栏推荐
- CCF BDCI hot topic: privacy information recognition in unstructured business text information
- Echart sets the spacing between columns
- Rongyun has completed several hundred million RMB round D financing, and will continue to build global cloud communication capability
- echart 设置柱子之间的间距
- Application and practice of native map and web fusion technology
- Some common types of error exception in Python
- 乘风破浪的技术大咖再次集结 | 腾讯云TVP持续航行中
- I do digital transformation in traditional industries (1)
- 详解三种不同的身份验证协议
- Full link stress testing of moral integrity -- the evolution of corpus intelligence
猜你喜欢

Cad2016 download autocad2016 download installation detailed tutorial CAD Download

实在是太棒了!阿里资深架构师20年经验整理分享ServiceMesh实战文档,涨薪就差这篇文章了!

Service registration and discovery of go micro integration Nacos

How to download and install autocad2020 in Chinese

Do programmers pay too much to work overtime? Should programmer's salary be reduced? Netizen: let go of other hard pressed programmers

It's amazing! Ali senior architect 20 years of experience, collate and share servicemesh actual combat documents, pay rise is bad for this article!

帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!

Data consistency of cache

MES system plays an important role in the factory production management

Cad2016 software installation tutorial
随机推荐
I interviewed a 33 year old Android programmer, who could only program for Baidu, but wanted 25K, which was met by me
A certification and authorization solution based on. Net core - hulutong 1.0 open source
Service registration and discovery of go micro integration Nacos
I heard that you changed your registered residence overnight. How can you help yourself if you work like ping?
echart 设置柱子之间的间距
最新版PyCharm 2020.3 :可实现结对编程,智能文本校对等|附下载体验
低功耗蓝牙单芯片为物联网助力
深入分析商淘多用户商城系统如何从搜索着手打造盈利点
Two ways for Tencent cloud server to build WordPress website
Why does it take more and more time to develop a software?
使用Fastai开发和部署图像分类器应用
Kubernetes V1.19.3 kubeadm 部署笔记(中)
Cad2016 download autocad2016 download installation detailed tutorial CAD Download
Openyurt in depth interpretation: how to build kubernetes native cloud edge efficient collaborative network?
微服务框架 Go-Micro 集成 Nacos 实战之服务注册与发现
spark学习(二)--作业调度和shuffle解析
jackson、fastjson、kryo、protostuff等序列化工具性能对比
js字符与ASCII码互转的方法
史上最惨黑客:偷走10亿美元比特币7年未花,最终被司法部全数缴获
MES system is different from traditional management in industry application