https://blog.csdn.net/u010961173/article/details/79769747
格式化輸出函數:printf/sprintf/fprintf/snprintf等等
一、打印函數簡介
作用:將“給定的內容”按照“指定的格式”輸出到“指定目標內”。
打印函數的基本格式:
char print_buf[BUF_SIZE]; void printf(const char *fmt, ...) { va_list ap;//定義一個指針變量 unsigned int i; va_start (ap, fmt); i = vsprintf (print_buf, sizeof(print_buf),fmt, ap); va_end (args); __put_char (print_buf,i); }
printf(const char *fmt,...)是一個可變參數函數,第一個參數為字符串,后面是格式化輸出參數列表。
c語言中函數的參數都是壓進棧里的,可變參數函數必須有一個參數表示參數的個數,才能讓編譯器知道要壓進棧多少參數,以及函數返回時彈出多少參數,printf(char *fmt,...)實現這個功能的是fmt字符串,里面有多少'%',就代表后面有多少個參數,所以我們必須提取出fmt字符串中'%'的個數,以及針對'%'后面不同的字符來處理參數。
const char *fmt定義了一個只讀字符指針;“...”表示printf的參數是可變的;
va_list ap:定義一個字符型指針變量
va_start(argv,i):初始化argv
c=va_arg(argv,int):在已知變量的情況下,獲得下一個變參變量
va_end(argv):結束變參變量操作
其中,__put_char()將字符逐個打印到串口輸出寄存器中。
void __put_char(char *p,int num){ while(*p&&num--){ *(volatile unsigned int *)0xd0000020=*p++; }; }
二、打印函數的實現
1、變參函數的實現(宏定義)
(1)va_list
typedef char * va_list; va_list ap; //定義一個指針類型
(2)va_start(ap,fmt)
#define va_start(ap,fmt) ( ap = (va_list)&fmt + _INTSIZEOF(fmt) )
ap指向函數棧中變參列表第一個參數的首地址;
____________________________
|___________________________|
| argn |
| ........ |
| arg0 |<----ap(sizeof(int)大小對齊)
| fmt |
| pc |
|___________________________|
注意:傳給宏va_start的參數fmt是可變參數列表中的前一個參數,ap指向變參列表中第一個參數地址。
注意:函數參數壓棧時,參數的入棧順序是從右向左,出棧時是從左向右。函數調用時,先把若干個參數都壓入棧中,再壓fmt,最后壓pc,這樣一來,棧頂指針偏移便找到了fmt,通過fmt中的%占位符,取得后面參數的個數,從而正確取得所有參數。
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
計算int類型按4字節(int占4字節)對齊后的結果。通過使用_INTSIZEOF(n),可以根據一個變量的類型計算變量在內存中占用的字節數,從而正確定位參數在內存的位置。
對於short、char類型的數據,因為不滿一個int類型的內存空間,所以按照int類型對齊;
(3)va_arg(ap,type)
#define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
指針變量ap本身指向變參列表中的下一個參數地址,va_arg取出的是當前參數的值
(4)va_end(ap)
#define va_end(ap) ( ap = (va_list)0 )
ap指向空
2、vsprintf(char *buf, const char *fmt, va_list args)函數實現
函數功能:將變量列表args中的參數按照fmt中規定的格式保存到臨時緩存buf中。
int vsprintf(char *buf, canst char *fmt, va_list args) { unsigned NUM_TYPE num; int base; char*str; int flags; int field_width; int Precision; int qualifier; str = bUf; for (; *fmt ; ++fmt) { if (* fmt ! = ' % ' ) { *str++ = *fmt; continue; } /* process flags */ flags = 0; repeat: ++fmt;/* skip first "%" */ switch(*fmt) { case '- ' : flags |= LEFT;goto repeat; case '+ ' : flags |= PLUS;goto repeat; ... } ... base = 10; switch (*fmt){ case 'c': ... *str++ (unsigned char)va_arg(args,int); ... continue; case 's': str = string(str,va_arg(args, char *),field_width,precision,flags); continue; ... case ' X ' : base = 16; break; case 'd': case ' i ' flags |= SIGN; case 'u': break; default: * str++ ='%'; if (*fmt) *str++ = *fmt; else --fmt ; continue; } str = number (str, num, base, field_width, precision, flags) ; } *str == '\0'; return str-buf; }
三、實現自己的打印函數
int vsnprintf(char *buf, int size, const char *fmt, va_list args){ int num; char *str, *end, c,*s; int read; unsigned int spec=0; str = buf;//臨時緩存buf首地址 end = buf + size;//臨時緩存buff結束地址 if (end < buf) { end = ((void *)-1); size = end - buf; } while (*fmt) { const char *old_fmt = fmt;//保存原來fmt的格式的首地址 read = <strong><span style="color:#ff0000;">format_decode</span></strong>(fmt, &spec);//判斷參數的格式,保存到spec中,read為當前參數在字符串中的指針偏移 fmt += read;//指針偏移到本參數格式的下一位字符的地址 if((FORMAT_TYPE(spec))==FORMAT_TYPE_NONE){ int copy = read; if (str < end) { if (copy > end - str)//防止buff空間不足越界 copy = end - str; memcpy(str, old_fmt, copy);//原樣拷貝到buff中 } str += read;//更新字符偏移 }else if(spec&FORMAT_FLAG_WIDTH){ //do nothing }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_CHAR){//字符類型,直接拷貝 c = (unsigned char) va_arg(args, int); if (str < end) *str = c; ++str; }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_STR){//字符串類型,直接拷貝 s = (char *) va_arg(args, char *); while(str<end&&*s!='\0'){ *str++=*s++; } }else{//數值型,進行轉換 if(FORMAT_TYPE(spec)==FORMAT_TYPE_INT){ num = va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_ULONG){ num = va_arg(args, unsigned long); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_LONG){ num = va_arg(args, long); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SIZE_T){ num = va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_USHORT){ num = (unsigned short) va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SHORT){ num = (short) va_arg(args, int); }else{ num = va_arg(args, unsigned int); } str=<strong><span style="color:#ff0000;">number</span></strong>(str,num,spec&FORMAT_BASE_MASK,spec); } } if (size > 0) { if (str < end) *str = '\0'; else end[-1] = '\0'; } return str-buf; }
format_decode(fmt, &spec);//判斷參數的格式,保存到spec中,read為當前參數在字符串中的指針偏移 fmt += read;//指針偏移到本參數格式的下一位字符的地址 if((FORMAT_TYPE(spec))==FORMAT_TYPE_NONE){ int copy = read; if (str < end) { if (copy > end - str)//防止buff空間不足越界 copy = end - str; memcpy(str, old_fmt, copy);//原樣拷貝到buff中 } str += read;//更新字符偏移 }else if(spec&FORMAT_FLAG_WIDTH){ //do nothing }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_CHAR){//字符類型,直接拷貝 c = (unsigned char) va_arg(args, int); if (str < end) *str = c; ++str; }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_STR){//字符串類型,直接拷貝 s = (char *) va_arg(args, char *); while(str<end&&*s!='\0'){ *str++=*s++; } }else{//數值型,進行轉換 if(FORMAT_TYPE(spec)==FORMAT_TYPE_INT){ num = va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_ULONG){ num = va_arg(args, unsigned long); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_LONG){ num = va_arg(args, long); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SIZE_T){ num = va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_USHORT){ num = (unsigned short) va_arg(args, int); }else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SHORT){ num = (short) va_arg(args, int); }else{ num = va_arg(args, unsigned int); } str=number(str,num,spec&FORMAT_BASE_MASK,spec); } } if (size > 0) { if (str < end) *str = '\0'; else end[-1] = '\0'; } return str-buf; }
(1)format_decode函數
作用:判斷格式化的符號及類型;
flags:第0字節:若為數值,作為數值基數;
第1字節:格式類型,字符,整形,長整型,短整型等;
第2字節:若為數值,表示數值符號;
int format_decode(const char *fmt,unsigned int *flags){ const char *start = fmt; *flags &= ~FORMAT_TYPE_MASK; *flags |= FORMAT_TYPE_NONE; for (; *fmt ; ++fmt) { if (*fmt == '%') break; } if (fmt != start || !*fmt) return fmt - start; do{ fmt++; switch(*fmt){ case 'l': SET_FORMAT_FLAG(*flags,FORMAT_FLAG_WIDTH); break; default: break; } }while(0); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); switch (*fmt) { case 'c': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_CHAR); break; case 's': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_STR); break; case 'o': SET_FORMAT_BASE(*flags,FORMAT_BASE_O); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'x': case 'X': SET_FORMAT_BASE(*flags,FORMAT_BASE_X); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'd': case 'i': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_INT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; case 'u': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; default: break; } return ++fmt-start;//參數偏移的字節數 } SET_FORMAT_FLAG(*flags,FORMAT_FLAG_WIDTH); break; default: break; } }while(0); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); switch (*fmt) { case 'c': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_CHAR); break; case 's': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_STR); break; case 'o': SET_FORMAT_BASE(*flags,FORMAT_BASE_O); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'x': case 'X': SET_FORMAT_BASE(*flags,FORMAT_BASE_X); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'd': case 'i': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_INT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; case 'u': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; default: break; } return ++fmt-start;//參數偏移的字節數 }
SET_FORMAT_FLAG(*flags,FORMAT_FLAG_WIDTH); break; default: break; } }while(0); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); switch (*fmt) { case 'c': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_CHAR); break; case 's': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_STR); break; case 'o': SET_FORMAT_BASE(*flags,FORMAT_BASE_O); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'x': case 'X': SET_FORMAT_BASE(*flags,FORMAT_BASE_X); SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); break; case 'd': case 'i': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_INT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; case 'u': SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT); SET_FORMAT_BASE(*flags,FORMAT_BASE_D); break; default: break; } return ++fmt-start;//參數偏移的字節數 }
(2)number函數
函數功能:根據類型進行數值轉換
char *number(char *str, int num,int base,unsigned int flags){ int i=0; int sign=0; if(FORMAT_SIGNED(flags)&&(signed int)num<0){ sign=1; num=~num+1; } do{ numbers[i++]=digits[do_div(num,base)]; }while(num!=0); if(FORMAT_BASE(flags)==FORMAT_BASE_O){ numbers[i++]='0'; }else if(FORMAT_BASE(flags)==FORMAT_BASE_X){ numbers[i++]='x'; numbers[i++]='0'; }else if(FORMAT_BASE(flags)==FORMAT_BASE_B){ numbers[i++]='b'; numbers[i++]='0'; } if(sign) numbers[i++]='-'; while (i-- > 0) *str++ = numbers[i]; return str; }
嵌入式操作系統---打印函數(printf/sprintf)的實現
格式化輸出函數:printf/sprintf/fprintf/snprintf等等
一、打印函數簡介
作用:將“給定的內容”按照“指定的格式”輸出到“指定目標內”。
打印函數的基本格式:
printf(const char *fmt,...)是一個可變參數函數,第一個參數為字符串,后面是格式化輸出參數列表。
c語言中函數的參數都是壓進棧里的,可變參數函數必須有一個參數表示參數的個數,才能讓編譯器知道要壓進棧多少參數,以及函數返回時彈出多少參數,printf(char *fmt,...)實現這個功能的是fmt字符串,里面有多少'%',就代表后面有多少個參數,所以我們必須提取出fmt字符串中'%'的個數,以及針對'%'后面不同的字符來處理參數。
const char *fmt定義了一個只讀字符指針;“...”表示printf的參數是可變的;
va_list ap:定義一個字符型指針變量
va_start(argv,i):初始化argv
c=va_arg(argv,int):在已知變量的情況下,獲得下一個變參變量
va_end(argv):結束變參變量操作
其中,__put_char()將字符逐個打印到串口輸出寄存器中。
void __put_char(char *p,int num){
while(*p&&num--){
*(volatile unsigned int *)0xd0000020=*p++;
};
}
二、打印函數的實現
1、變參函數的實現(宏定義)
(1)va_list
(2)va_start(ap,fmt)
#define va_start(ap,fmt) ( ap = (va_list)&fmt + _INTSIZEOF(fmt) )
ap指向函數棧中變參列表第一個參數的首地址;
____________________________
|___________________________|
| argn |
| ........ |
| arg0 |<----ap(sizeof(int)大小對齊)
| fmt |
| pc |
|___________________________|
注意:傳給宏va_start的參數fmt是可變參數列表中的前一個參數,ap指向變參列表中第一個參數地址。
注意:函數參數壓棧時,參數的入棧順序是從右向左,出棧時是從左向右。函數調用時,先把若干個參數都壓入棧中,再壓fmt,最后壓pc,這樣一來,棧頂指針偏移便找到了fmt,通過fmt中的%占位符,取得后面參數的個數,從而正確取得所有參數。
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
計算int類型按4字節(int占4字節)對齊后的結果。通過使用_INTSIZEOF(n),可以根據一個變量的類型計算變量在內存中占用的字節數,從而正確定位參數在內存的位置。
對於short、char類型的數據,因為不滿一個int類型的內存空間,所以按照int類型對齊;
(3)va_arg(ap,type)
#define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
指針變量ap本身指向變參列表中的下一個參數地址,va_arg取出的是當前參數的值
(4)va_end(ap)
#define va_end(ap) ( ap = (va_list)0 )
ap指向空
2、vsprintf(char *buf, const char *fmt, va_list args)函數實現
函數功能:將變量列表args中的參數按照fmt中規定的格式保存到臨時緩存buf中。
三、實現自己的打印函數
int vsnprintf(char *buf, int size, const char *fmt, va_list args){
int num;
char *str, *end, c,*s;
int read;
unsigned int spec=0;
str = buf;//臨時緩存buf首地址
end = buf + size;//臨時緩存buff結束地址
if (end < buf) {
end = ((void *)-1);
size = end - buf;
}
while (*fmt) {
const char *old_fmt = fmt;//保存原來fmt的格式的首地址
read = <strong><span style="color:#ff0000;">format_decode</span></strong>(fmt, &spec);//判斷參數的格式,保存到spec中,read為當前參數在字符串中的指針偏移
fmt += read;//指針偏移到本參數格式的下一位字符的地址
if((FORMAT_TYPE(spec))==FORMAT_TYPE_NONE){
int copy = read;
if (str < end) {
if (copy > end - str)//防止buff空間不足越界
copy = end - str;
memcpy(str, old_fmt, copy);//原樣拷貝到buff中
}
str += read;//更新字符偏移
}else if(spec&FORMAT_FLAG_WIDTH){
//do nothing
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_CHAR){//字符類型,直接拷貝
c = (unsigned char) va_arg(args, int);
if (str < end)
*str = c;
++str;
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_STR){//字符串類型,直接拷貝
s = (char *) va_arg(args, char *);
while(str<end&&*s!='\0'){
*str++=*s++;
}
}else{//數值型,進行轉換
if(FORMAT_TYPE(spec)==FORMAT_TYPE_INT){
num = va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_ULONG){
num = va_arg(args, unsigned long);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_LONG){
num = va_arg(args, long);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SIZE_T){
num = va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_USHORT){
num = (unsigned short) va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SHORT){
num = (short) va_arg(args, int);
}else{
num = va_arg(args, unsigned int);
}
str=<strong><span style="color:#ff0000;">number</span></strong>(str,num,spec&FORMAT_BASE_MASK,spec);
}
}
if (size > 0) {
if (str < end)
*str = '\0';
else
end[-1] = '\0';
}
return str-buf;
}format_decode(fmt, &spec);//判斷參數的格式,保存到spec中,read為當前參數在字符串中的指針偏移
fmt += read;//指針偏移到本參數格式的下一位字符的地址
if((FORMAT_TYPE(spec))==FORMAT_TYPE_NONE){
int copy = read;
if (str < end) {
if (copy > end - str)//防止buff空間不足越界
copy = end - str;
memcpy(str, old_fmt, copy);//原樣拷貝到buff中
}
str += read;//更新字符偏移
}else if(spec&FORMAT_FLAG_WIDTH){
//do nothing
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_CHAR){//字符類型,直接拷貝
c = (unsigned char) va_arg(args, int);
if (str < end)
*str = c;
++str;
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_STR){//字符串類型,直接拷貝
s = (char *) va_arg(args, char *);
while(str<end&&*s!='\0'){
*str++=*s++;
}
}else{//數值型,進行轉換
if(FORMAT_TYPE(spec)==FORMAT_TYPE_INT){
num = va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_ULONG){
num = va_arg(args, unsigned long);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_LONG){
num = va_arg(args, long);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SIZE_T){
num = va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_USHORT){
num = (unsigned short) va_arg(args, int);
}else if(FORMAT_TYPE(spec)==FORMAT_TYPE_SHORT){
num = (short) va_arg(args, int);
}else{
num = va_arg(args, unsigned int);
}
str=number(str,num,spec&FORMAT_BASE_MASK,spec);
}
}
if (size > 0) {
if (str < end)
*str = '\0';
else
end[-1] = '\0';
}
return str-buf;
}
(1)format_decode函數
作用:判斷格式化的符號及類型;
flags:第0字節:若為數值,作為數值基數;
第1字節:格式類型,字符,整形,長整型,短整型等;
第2字節:若為數值,表示數值符號;
SET_FORMAT_FLAG(*flags,FORMAT_FLAG_WIDTH);
break;
default:
break;
}
}while(0);
SET_FORMAT_BASE(*flags,FORMAT_BASE_D);
switch (*fmt) {
case 'c':
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_CHAR);
break;
case 's':
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_STR);
break;
case 'o':
SET_FORMAT_BASE(*flags,FORMAT_BASE_O);
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT);
break;
case 'x':
case 'X':
SET_FORMAT_BASE(*flags,FORMAT_BASE_X);
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT);
break;
case 'd':
case 'i':
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_INT);
SET_FORMAT_BASE(*flags,FORMAT_BASE_D);
break;
case 'u':
SET_FORMAT_TYPE(*flags,FORMAT_TYPE_UINT);
SET_FORMAT_BASE(*flags,FORMAT_BASE_D);
break;
default:
break;
}
return ++fmt-start;//參數偏移的字節數
}
(2)number函數
函數功能:根據類型進行數值轉換
