嵌入式linux学习笔记--20200223--可变参数函数--单片机的printf函数的实现

记录一下  今天看到的关于printf的实现机制, 了解到了C语言中的可变参函数的一些实现机制。

我们经常调用的一个函数  printf()函数

这是在linux 下  man printf 的结果。

在printf 传入的参数的第二个参数    数字1    就是一个可选的参数,  也是我们今天的主角可变参数  第一个强制参数后面的 三个点,就是可能存在的参数。

可变参数之所以能实现是靠的 C语言对于函数实现的   这里的实现机制是这样的   以下来自

https://blog.csdn.net/weixin_44692935/article/details/103001787   

根据 压栈和出栈的逻辑我们可以向函数内传递不确定个数的参数,当调用函数   fun(a,b,c,d)   的时候

参数 d ,c,b,a 依次压入 栈  我们能直接确定的参数是强制参数 a   ,根据a 的 地址以及a的数据类型(int  char  double 等等) 我们可以确定b 的地址,接着根据b的地址以及参数b的数据类型  我们可以确定下一个参数的地址。 

根据我们上面说的这些 ,有一个信息至关重要,那就是我们可选参数的数量, 这个数量信息必须要体现在第一个强制参数里面

还是拿 刚刚那位大佬的  那篇博客做例子  https://blog.csdn.net/weixin_44692935/article/details/103001787

他的这篇博客的示例代码  第一个参数直接就代表着 传入的可变参数数量。

我们在常用的 printf 中的第一部分字符串信息  直接就有  %d   %c  %s 等等   printf函数 通过分析这些字符串中的  % 后面接的 字符就可以确定 参数的数量 以及要显示的格式 等信息。

我自己总结就是第一个参数一定不可缺少, 第一个参数一定要包含后面参数的个数的信息  。其余的数据类型  也可以在函数定义的时候确定  。

关于自己写 可变参数函数  大家可以参考百问网 提供的代码 

代码如下

 
#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);
	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);
	int count=0,i=0;
			

	*--s = '\0';
	
	if (n < 0){
		m = -n;
	}
	else{
		m = n;
	}
	
	do{
		*--s = hex_tab[m%base];
		count++;
	}while ((m /= base) != 0);
	
	if( maxwidth && count < maxwidth){
		for (i=maxwidth - count; i; i--)	
			*--s = lead;
}

	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;
			}
			
		//format : %08d, %8d,%d,%u,%x,%f,%c,%s 
		    fmt++;
		if(*fmt == '0'){
			lead = '0';
			fmt++;	
		}

		lead=' ';
		maxwidth=0;
		
		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("This is www.100ask.org   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",    "www.100ask.org") ;	
	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;
}




#ifndef _MY_PRINTF_H
#define _MY_PRINTF_H

#include "uart.h"
#define  __out_putchar  putchar


#define  MAX_NUMBER_BYTES  64

extern int my_printf_test(void);
int printf(const char *fmt, ...);

#endif /* _MY_PRINTF_H */

#define va_arg(ap,t)    ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) )

这句话比较难以理解 是一个逗号表达式  (表达式1,表达式2)

先去执行表达式1 然后执行表达式2      返回值是表达式2的运算结果  

截图来自韦东山老师的新一期视频

发布了35 篇原创文章 · 获赞 30 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_41534481/article/details/104458477