嵌入式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的運算結果  

截圖來自韋東山老師的新一期視頻

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章