【程设大作业】printf 的实现

我决定挂(biao)一挂(biao)我们的这个程设大作业。
(同样是大一,别人家的大作业是写一个 jumping game,怎么到你这就是个 printf 呢。。。

Task

       ~~~~~~       一句话,就是要手写 printf。
       ~~~~~~       具体来讲,你需要自己实现一个函数(C 语言),名叫 myprintf,其功能和 printf 一致——参数第一个是字符串format[],后面是任意个参数,然后能把这些东西输出出来,返回值是一共输出了多少个字符。
       ~~~~~~       当然你不能在 myprintf 里内嵌 printf,只能用 putchar。
       ~~~~~~       为了简化问题,对于 % 后面的转换字符只作部分规定(见下表),其他如 %lld、%a 等视为未定义。下面是题目的部分pdf:在这里插入图片描述在这里插入图片描述
在这里插入图片描述

考点

       ~~~~~~       第一自然是可变参列表的使用了。
       ~~~~~~       第二就是对format这个字符串的处理。
       ~~~~~~       第三就是如何输出这些参数

       ~~~~~~       隐藏的第四点是你要发现 printf 的所有隐藏鬼畜

sol

可变参列表

       ~~~~~~       头文件是:

#include<stdarg.h>

       ~~~~~~       参数数量、类型是不定的,故曰可变参列表,用三个点来表示:

int myprintf(const char format[], ...)
{
}

然后你要需要一个指向参数的指针:

va_list ap;             // 定义指针ap
va_start(ap,format);    // 初始化ap,将它指向format的末尾,这样它后面就是那个...列表了。

接着就是通过指针ap提取列表里的参数,例如左到右依次是 int、char、double、字符串,那么可以这样:

int a=va_arg(ap,int);    // 执行完这个操作之后ap会自动往后跳,比如这里就会跳到这个int的末尾
char b=va_arg(ap,int);   // 因为char也是int。。。比较奇怪的设定。。
double c=va_arg(ap,double);
char *d=va_arg(ap,char*);

用完了之后要把ap释放掉:

va_end(ap);

字符串处理

       ~~~~~~       这个瞎处理就好了,一位一位扫format,碰到 % 就后面接一堆判断和 switch,否则就直接输出。
       ~~~~~~       至于许多小朋友们担心的 \n、\t、\233 等转义字符怎么办,其实 C 已经自动帮你转好的了,它们本身就只是一个字符而已。

输出参数

       ~~~~~~       有机智的小朋友说直接 _vsnprintf 就好啦。。。快去请卢来佛祖
       ~~~~~~       基本的思想都是把数字转化成数组,然后倒着输出。这个大家在学进制转换的时候天天玩的了。
       ~~~~~~       然后为了方便统计一共输出了多少字符,建议最后的输出统一用字符串进行。即把数组也再转换成字符串,丢到统一的输出函数里去。

       ~~~~~~       麻烦的是实数,你不能把任何一个部分转成整数来处理,因为会爆 long long。然后你还要保持精度。
       ~~~~~~       所以要想个办法。可以先转成科学记数法,意思就是对于实数 x x x 要先求出一个最大的 10 10 10 的幂 e p ep ep 使得 x e p ∈ [ 1 , 10 ) \frac{x}{ep}\in[1,10) epx[1,10),然后从高位开始一位一位除下去,存到数组里。当然,视不同的输出格式要做些调整,比如 %f 输出的时候无视 e p &lt; 1 ep&lt;1 ep<1 的情况。
       ~~~~~~       另一个办法是 C 里有一个库函数可以直接把实数转成字符串。但是这个精度不好,输出 2 63 2^{63} 263 的时候最后一位就被吞掉了。所以不推荐。
       ~~~~~~       注意四舍五入,这里会有一个类似于高精度的运算。(恶心吧。。

鬼畜区

       ~~~~~~       真正实现起来的时候,你会发现 printf 是有多么的鬼畜。。。

       ~~~~~~       四舍五入对吧,好那我就四舍五入。诶为什么printf("%.f",3.5);输出 3 啊???
       ~~~~~~       诶为什么printf("%.f",3.55);就是 4 了啊???
       ~~~~~~       再输出一个printf("%.f",3.5000001);,懂了,这逼居然是恰好 0.5 的时候不进位,要严格大于 0.5 才进位。。。

       ~~~~~~       好的现在肝完 %f 和 %e 来肝 %g 了。
       ~~~~~~       诶不对为啥 printf("%.6g",0.00001234);是 1.234e-005 啊??不是说好了默认 6 位精度的吗??诶为啥printf("%.4g",acos(-1));是 3.142 啊这个精度也不对啊??
       ~~~~~~       输出研究了一通,%f 和 %e 的精度是指小数点后保留多少位,%g 的精度是指有效数字。。。

       ~~~~~~       好的这些都只是小鬼畜。我们试着输出一发:

int haha=2147483647;
double tst=3.6;
printf("%yha  %.yha %33.33ttttt  %%..... %%d %hd\n",haha,haha);

在这里插入图片描述
       ~~~~~~       诶这啥玩意???
       ~~~~~~       再来。。

double tst=3.6;
printf("%...f %----10.f\n",tst,tst);

在这里插入图片描述
       ~~~~~~       这。。。
       ~~~~~~       自动去掉重复的东西??

printf("%.---3f\n",tst);

在这里插入图片描述
       ~~~~~~       这。。。
       ~~~~~~       精度自动取绝对值,好像都能解释,没什么问题。。那大概就是这样了吧,验证下。。

printf("%----10.....----10f\n",tst);

在这里插入图片描述
       ~~~~~~       ?????????????????

       ~~~~~~       弃疗

代码

话说为什么 csdn 的代码框变得这么丑。。再这样我要搬家了。。
(upd:居然要在```后面加语种。。。

先是 myprintf

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<stdarg.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxlen 1000005

typedef unsigned int uint;

const double eps=1e-14;

int max(int x,int y) {return (x>y) ?x :y ;}

int charnum,d0,d[maxlen],len,ali,width;
char S[maxlen];
void Put(char ch)
{
	putchar(ch);
	charnum++;
}

void PutString(char *S,int limit)
{
	int len=strlen(S);
	if (limit>-1 && limit<len) len=limit;
	if (ali) fo(i,1,width-len) Put(' ');
	fo(i,0,len-1) Put(S[i]);
	if (!ali) fo(i,1,width-len) Put(' ');
}

void PutChar(char ch)
{
	S[0]=ch, S[1]='\000';
	PutString(S,-1);
}

void PutInt(int x,int limit)
{
	if (x<0)
	{
		S[len++]='-';
		x=-x;
	}
	if (x==0) d[d0=1]=0;
		else for(d0=0; x; x/=10) d[++d0]=x%10;
	
	fo(i,1,limit-d0) S[len++]='0';
	fd(i,d0,1) S[len++]=d[i]+'0';
	S[len++]='\000';
	PutString(S,-1);
}

void PutUInt(uint x,int ty,int cap,int limit)
{
	if (x==0) d[d0=1]=0;
		else for(d0=0; x; x/=ty) d[++d0]=x%ty;
	
	fo(i,1,limit-d0) S[len++]='0';
	fd(i,d0,1) S[len++]=(d[i]<10) ?d[i]+'0' :d[i]-10+(cap?'A':'a');
	S[len++]='\000';
	PutString(S,-1);
}

void DigitToArray(double x,double ep,int len)
{
	d[d0=0]=0;
	fo(i,1,len)
	{
		d[++d0]=fmod(x,ep*10)/ep;
		x-=d[d0]*ep;
		ep/=10;
	}
	d[d0]+=(fmod(x,ep*10)/ep>5+eps);
	for(int i=d0; d[i]>9; i--) d[i]-=10, d[i-1]++;
}

void PutFloat(double x,int acr)
{
	if (x<0)
	{
		S[len++]='-';
		x=-x;
	}
	
	int w=1;
	double ep=1;
	for(double xx=x; xx+eps>=10; xx/=10) w++, ep*=10;
	DigitToArray(x,ep,w+acr);
	
	int st=(d[0]==0);
	fo(i,st,w) S[len++]=d[i]+'0';
	if (acr)
	{
		S[len++]='.';
		fo(i,w+1,w+acr) S[len++]=d[i]+'0';
	}
	S[len++]='\000';
	PutString(S,-1);
}

void PutFloatE(double x,int acr,char cap)
{
	if (x<0)
	{
		S[len++]='-';
		x=-x;
	}
	
	int index=0;
	double ep=1;
	for(double xx=x; xx+eps>=10; xx/=10) index++, ep*=10;
	for(double xx=x; xx>eps && xx+eps<1; xx*=10) index--, ep/=10;
	DigitToArray(x,ep,1+acr);
	index+=(d[0]>0);
	
	int st=(d[0]==0);
	S[len++]=d[st]+'0';
	if (acr)
	{
		S[len++]='.';
		fo(i,st+1,st+acr) S[len++]=d[i]+'0';
	}
	S[len++]=cap;
	if (index>=0) S[len++]='+'; else S[len++]='-', index=-index;
	for(int i=100; i>=1; i/=10) S[len++]=(index/i)%10+'0';
	S[len]='\000';
	PutString(S,-1);
}

void PutFloatG(double x,int acr,char cap)
{
	if (x<0)
	{
		S[len++]='-';
		x=-x;
	}
	
	int index=0;
	double ep=1;
	for(double xx=x; xx+eps>=10; xx/=10) index++, ep*=10;
	for(double xx=x; xx>eps && xx+eps<1; xx*=10) index--, ep/=10;
	DigitToArray(x,ep,acr);
	index+=(d[0]>0);
	
	int st=(d[0]==0);
	if (index<-4 || index>=acr)
	{
		while (st<d0 && d[d0]==0) d0--;
		S[len++]=d[st]+'0';
		if (st<d0)
		{
			S[len++]='.';
			fo(i,st+1,d0) S[len++]=d[i]+'0';
		}
		S[len++]=cap;
		if (index>=0) S[len++]='+'; else S[len++]='-', index=-index;
		for(int i=100; i>=1; i/=10) S[len++]=(index/i)%10+'0';
	} else
	{
		int w=max(index+1,0);
		while (st+w-1<d0 && d[d0]==0) d0--;
		if (!w) S[len++]='0';
			else fo(i,st,st+w-1) S[len++]=d[i]+'0';
		if (st+w-1<d0) S[len++]='.';
		fo(i,1,-index-1) S[len++]='0';
		fo(i,st+w,d0) S[len++]=d[i]+'0';
	}
	S[len]='\000';
	PutString(S,-1);
}

int myprintf(const char format[],...)
{
	int n=strlen(format);
	charnum=0;
	va_list ap;
	va_start(ap,format);
	
	fo(i,0,n-1) if (format[i]=='%' && i<n-1)
	{
		int now=i;
		
		ali=1, width=0;
		int acr=0, hasacr=0, h=0, l=0, get;
		while (format[i+1]=='-') ali=0, i++;
		while (format[i+1]>='0' && format[i+1]<='9') width=width*10+format[++i]-'0';
		if (format[i+1]=='.') hasacr=1, i++;
		if (format[i+1]=='-') i++;
		while (format[i+1]>='0' && format[i+1]<='9') acr=acr*10+format[++i]-'0';
		if (format[i+1]=='h') h=1, i++;
		if (format[i+1]=='l') l=1, i++;
		
		len=0;
		switch (format[++i]) {
			case 'd': case 'i':
				get= h ?(short)va_arg(ap,int) :va_arg(ap,int) ;
				PutInt(get,acr);
				break;
			case 'o':
				get= h ?(short)va_arg(ap,uint) :va_arg(ap,uint) ;
				PutUInt(get,8,0,acr);
				break;
			case 'x': case 'X':
				get= h ?(short)va_arg(ap,uint) :va_arg(ap,uint) ;
				PutUInt(get,16,(format[i]=='X'),acr);
				break;
			case 'u':
				get= h ?(short)va_arg(ap,uint) :va_arg(ap,uint) ;
				PutUInt(get,10,0,acr);
				break;
			case 'c':
				PutChar(va_arg(ap,int));
				break;
			case 's':
				PutString(va_arg(ap,char*),(hasacr)?acr:-1);
				break;
			case 'f':
				PutFloat(va_arg(ap,double),(hasacr)?acr:6);
				break;
			case 'e': case 'E':
				PutFloatE(va_arg(ap,double),(hasacr)?acr:6,format[i]);
				break;
			case 'g': case 'G':
				PutFloatG(va_arg(ap,double),(hasacr)?max(acr,1):6,format[i]-2);
				break;
			case 'p':
				PutUInt((uint)va_arg(ap,void*),16,0,8);
				break;
			case '%':
				Put('%');
				break;
			default:
				fo(j,now,i) Put(format[j]);
		}
	} else Put(format[i]);
	
	va_end(ap);
	return charnum;
}

然后是一些测试

#include"myprintf.c"

int main()
{
	//freopen("main.out","w",stdout);
	
	long long ll=2147483647; ll++; ll=ll*ll; ll=ll-1+ll;
	printf("%e\n",acos(-1)-3);
	myprintf("%e\n",acos(-1)-3);
	printf("%f %f %f\n",-acos(-1),(double)ll,1.0/22);
	myprintf("%f %f %f\n",-acos(-1),(double)ll,1.0/22);
	puts("");
	
	int a=20, b=30, n;
	double c=10.5, d=100000000;
	char e='m';
	char s[9]="myprintf";
	myprintf("%d %d\n",a,a+b);
	myprintf("%f %e\n",c,d);
	n=myprintf("%c\t%s\n",e,s);
	myprintf("%d\n",n);
	puts("");
	
	printf("%g %g %.2g %.2g\n", 0.00001234,0.0001234,123.45,23.45);
	myprintf("%g %g %.2g %.2g\n", 0.00001234,0.0001234,123.45,23.45);
	float x=654.321; double pi=acos(-1);
	printf("%f %e %g %.8f %.8e %.8g %.4g\n",x,x,x,pi,pi,pi,pi);
	myprintf("%f %e %g %.8f %.8e %.8g %.4g\n",x,x,x,pi,pi,pi,pi);
	printf("%g %.5g %.3g\n",0.034,9999.99,0.0009999);
	myprintf("%g %.5g %.3g\n",0.034,9999.99,0.0009999);
	puts("");
	
	int haha=2147483647; double tst=3.6;
	printf("%yha  %.yha %33.33ttttt  %%..... %%d %hd\n",haha,haha);
	myprintf("%yha  %.yha %33.33ttttt  %t  %..... %%d %hd\n",haha,haha);
	printf("%33.33\n");
	myprintf("%33.33\n");
	
	printf("%----10.....----10f\n",tst);
	myprintf("%----10.....----10f\n",tst);
	printf("%-10..3f\n",tst);
	myprintf("%-10..3f\n",tst);
	printf("%...f %----10.f\n",tst,tst);
	myprintf("%...f %----10.f\n",tst,tst);
	printf("%.---3f\n",tst);
	myprintf("%.---3f\n",tst);
	printf("%d\n",printf("\0"));
	printf("%d\n",myprintf("\0"));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值