自己实现printf函数

205 篇文章 9 订阅
195 篇文章 9 订阅

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;
}
                             


 

实验结果如下:

  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值