当前位置:网站首页>自己实现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
边栏推荐
- arthas无网络环境下离线安装方法
- 5 minutes get I use GitHub's 5-year summary of these operations!
- JS method of judging object type_ How to use typeof_ How to use instanceof_ How to use constructor_ Object.prototype.toString How to use ()
- 高德全链路压测——语料智能化演进之路
- Application and practice of native map and web fusion technology
- A letter to myself
- spark学习(二)--作业调度和shuffle解析
- Hadoop learning (3) - Yarn
- 帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!
- H5公众号点击内置浏览器的关闭(左上角的叉叉)监听到事件
猜你喜欢

超大折扣力度,云服务器 88 元秒杀

程序员过高工资导致加班?应该降低程序员工资?网友:放过其他苦逼的程序员吧

I do digital transformation in traditional industries (1)

Notes for csp-j / s 2020

5分钟GET我使用Github 5 年总结的这些骚操作!

MES system is different from traditional management in industry application

CAD2020下载AutoCAD2020下载安装教程AutoCAD2020中文下载安装方法

How to use Camtasia to make dynamic animation scene?

Service registration and discovery of go micro integration Nacos

Position promotion | intelligent multimedia group of Microsoft Asia research institute recruits computer vision algorithm Intern
随机推荐
融云完成数亿人民币 D 轮融资,将持续打造全球云通信能力
Leetcode algorithm (1)
程序员过高工资导致加班?应该降低程序员工资?网友:放过其他苦逼的程序员吧
AUTOCAD2020安装包&安装教程
深入分析商淘多用户商城系统如何从搜索着手打造盈利点
Echart sets the spacing between columns
Mobile security reinforcement helps app achieve comprehensive and effective security protection
Using fastai to develop and deploy image classifier application
echart 设置柱子之间的间距
你的钱为什么会被转走,这篇文章告诉你答案
乘风破浪的技术大咖再次集结 | 腾讯云TVP持续航行中
Get this template, double your salary
帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!
A certification and authorization solution based on. Net core - hulutong 1.0 open source
史上最惨黑客:偷走10亿美元比特币7年未花,最终被司法部全数缴获
ImmutableMap的put方法问题
Some common types of error exception in Python
C语言—————三子棋游戏
MES系统在行业应用里区别于传统式管理
听说你一夜之间变了户籍,依萍如洗的打工人该如何自救?