栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

自己用C语言实现printf

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

自己用C语言实现printf

本文参考:https://www.pianshen.com/article/35981882012/

前提的头文件:

//==================================================================================================
typedef char* va_list;
#define _INTSIZEOF1(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF1(v) )  
//ap指向fmt后面的地址
//#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_arg(ap,t)    (*(t *)( ap=ap + _INTSIZEOF1(t), ap- _INTSIZEOF1(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' };         
//==================================================================================================

一、可变参数的函数是怎么传参的?

我们定义:

int printf0(const char* fmt, ...)
{
	va_list ap;
	va_start(ap,fmt)
	my_vprintf(fmt, ap);
	va_end(ap);
	return 0;
}

其中比较难理解的就是

va_start(ap,fmt)
va_arg(ap,t)

这两个函数是什么意思,原文也解释的不太清楚

研究了一下,可变参数的函数可以看成是一串保持参数地址的字符串,占用一片连续的内存,printf可以看成是类似char**,如下图所示:

这个图很好地说明了问题,fmt的地址,参数a的地址,参数A的地址(图里的0x0098f914这列地址)是连续的,我们用ap指向了这些地址:

 ( ap = (va_list)&v + _INTSIZEOF1(v) ) 

也就是说ap的内容为0x0098f918,指向了第一个参数a。

假设传入一串参数:

printf0("test=%c,%c,%cnr", 'a', 'A','F');

那么fmt地址指向字符串

test=%c,%c,%cnr

ap指向字符串

‘a’

如果ap想指向下一个变量A,只需要ap++即可。

二、const char*是怎么打印的?

我们传入了fmt,fmt的地址为(0x00b07de8)只要++用putc输出即可,注意到这里putc是输入的ascii码对应的十进制值

static int outc(int c)
{
	
	putchar(c);                                        //这里的_out_putchar其实就是putchar,在.h中定义
	return 0;
}

三、遇到参数怎么办?
先识别是不是%,如果不是那么就用for循环照常打印fmt:

如果是,那么fmt++跳过%,将ap先指向下一个变量再返回ap-4(当前变量的地址),根据类型分类判断打印出来;

for (; *fmt != ''; 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_arg111(&ap)); break;

		//case 'c': outc(va_arg(ap, int)); break;
		case 's': outs(va_arg(ap, char*)); break;

		default:
			outc(*fmt);
			break;
		}
	}

以下是全部代码:

#include



//==================================================================================================
typedef char* va_list;
#define _INTSIZEOF1(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF1(v) )   //ap指向fmt后面的地址
//#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_arg(ap,t)    ( *(t *)                           ( ap=ap + _INTSIZEOF1(t), ap- _INTSIZEOF1(t)     )             )

//#define va_arg(ap,t)    ( *(t *) ap )
#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)
{
	
	putchar(c);                                        //这里的_out_putchar其实就是putchar,在.h中定义
	return 0;
}

static int outs(const char* s)                                 //输出字符串
{
	while (*s != '')
		putchar(*s++);
	return 0;
}

static int out_num(long n, int base, char lead, int maxwidth)
{
	unsigned long m = 0;
	char buf[256], * s = buf + sizeof(buf);    // sizeof算结束符'' ,strlen不算
	int count = 0, i = 0;				//注意这里s指向buf的末端,至于为什么继续往下看	


	*--s = '';                               //先--,在赋值结束符,因为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);
}
//#define va_arg(ap,t)    ( *(t *) ( ap=ap + _INTSIZEOF1(t), ap- _INTSIZEOF1(t)     )  
int va_arg111(va_list * ap)
{

	*ap = *ap +4;
	//printf("ap+4 va_arg is %pn", *ap);
	return (*(int*)(*ap - 4));
}


static int my_vprintf(const char* fmt, va_list ap)
{
	char lead = ' ';
	int  maxwidth = 0;

	for (; *fmt != ''; 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_arg111(&ap)); 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 printf0(const char* fmt, ...)
{
	va_list ap;
		
	//va_start(ap, fmt);
	ap = (va_list)&fmt;
	//printf("ap add: %pn", ap);
	//printf("fmt : %pn", fmt);
	//printf("&fmt : %pn", &fmt);
	ap = (va_list)&fmt + _INTSIZEOF1(fmt);
	//printf("*ap : %cn", *ap);

	
	
	//printf("ap add: %pn", &ap);
	//printf("ap : %cn", ap);
	my_vprintf(fmt, ap);
	va_end(ap);
	return 0;
}


int my_printf_test(void)
{
	printf("%dn", sizeof(char*));
	//printf0("My_printf testnr");
	printf0("test=%c,%c,%cnr", 'A', 'a','F');
	printf0("test decimal number =%dnr", 123456);
	printf0("test decimal number =%dnr", -123456);
	printf0("test hex     number =0x%xnr", 0x55aa55aa);
	printf0("test string         =%snr", "yoyoyo");
	printf0("num=%08dnr", 12345);
	printf0("num=%8dnr", 12345);
	printf0("num=0x%08xnr", 0x12345);
	printf0("num=0x%8xnr", 0x12345);
	printf0("num=0x%02xnr", 0x1);
	printf0("num=0x%2xnr", 0x1);

	printf0("num=%05dnr", 0x1);
	printf0("num=%5dnr", 0x1);

	return 0;
}

int main()
{
	my_printf_test();
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/352477.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号