嵌入式操作系统---打印函数(printf/sprintf)的实现
2018-03-31 23:22
471 查看
格式化输出函数:printf/sprintf/fprintf/snprintf等等
一、打印函数简介
作用:将“给定的内容”按照“指定的格式”输出到“指定目标内”。
打印函数的基本格式:
void printf(const char *fmt, ...)
{
va_list args;//定义一个指针变量
unsigned int i;
va_start (args, fmt);
i = vsprintf (print_buf, sizeof(print_buf),fmt, args);
va_end (args);
__put_char (print_buf,i);、
}
const char *fmt定义了一个只读字符指针;“...”表示printf的参数是可变的;
va_list argv:定义一个变参变量
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)_INTSIZEOF(n)
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
计算各种数据类型按4字节(int占4字节)对齐后的结果。通过使用_INTSIZEOF(n),可以根据一个变量的类型计算变量载内存中占用的字节数,从而正确定位参数在内存的位置。
对于short、char类型的数据,因为不满一个int类型的内存空间,所以按照int类型对齐;
(3)va_start(ap,v)
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
ap指向变参列表的首地址指针;
注意:传给宏va_start的参数v都是可变参数列表中的前一个参数,即ap指向的是前一个参数紧跟后面那个参数对应的首地址(这个参数即为变参列表的第一个参数),即当前参数的地址。
(4)va_arg(ap,t)
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
更新变量ap指向列表中的下一个参数,然后通过偏移取出变量ap所指向的上一个参数地址(为偏移前的参数)
(5)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 = 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;//参数偏移的字节数
}
(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+
4000
+]='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;
}
一、打印函数简介
作用:将“给定的内容”按照“指定的格式”输出到“指定目标内”。
打印函数的基本格式:
void printf(const char *fmt, ...)
{
va_list args;//定义一个指针变量
unsigned int i;
va_start (args, fmt);
i = vsprintf (print_buf, sizeof(print_buf),fmt, args);
va_end (args);
__put_char (print_buf,i);、
}
const char *fmt定义了一个只读字符指针;“...”表示printf的参数是可变的;
va_list argv:定义一个变参变量
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)_INTSIZEOF(n)
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
计算各种数据类型按4字节(int占4字节)对齐后的结果。通过使用_INTSIZEOF(n),可以根据一个变量的类型计算变量载内存中占用的字节数,从而正确定位参数在内存的位置。
对于short、char类型的数据,因为不满一个int类型的内存空间,所以按照int类型对齐;
(3)va_start(ap,v)
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
ap指向变参列表的首地址指针;
注意:传给宏va_start的参数v都是可变参数列表中的前一个参数,即ap指向的是前一个参数紧跟后面那个参数对应的首地址(这个参数即为变参列表的第一个参数),即当前参数的地址。
(4)va_arg(ap,t)
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
更新变量ap指向列表中的下一个参数,然后通过偏移取出变量ap所指向的上一个参数地址(为偏移前的参数)
(5)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 = 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;//参数偏移的字节数
}
(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+
4000
+]='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()函数实现串口打印的配置方法
- 嵌入式 实现自己的printf函数小示例
- 三个打印函数printf()/sprintf()/snprintf()区别
- 一步步学嵌入式操作系统笔记4---- printf中va_list的实现
- 一步一步写嵌入式操作系统5—变参函数的原理和实现1 转
- USART打印消息--实现类似printf()的函数
- newlib 中的打印函数printf,sprintf及asprintf
- 使用printf函数实现串口信息打印——设置IAR和Keil的Options
- 简易printf打印实现,占用内存非常小------<嵌入式开发自学笔记>
- C打印函数printf的一种实现原理简要分析
- C打印函数printf的一种实现原理简要分析
- 实现自己的嵌入式printf函数
- C语言之linux内核可变参实现printf,sprintf
- 嵌入式 linux编程对sys中printf的外部包装以及打印开关设计
- printf函数实现的深入剖析
- 可移植的单片机printf(print by format)函数实现
- 转:printf()和sprintf()函数的区别和用法
- Go打印函数printf格式简介
- 1、通过函数实现打印*号组成的直角三角形,函数要求传入行数即可。在main 方法中,通过用户输入得到行数,然后调用函数做打印。 三角形样式: * ** *** **** ***** ******
- php中sprintf与printf函数用法区别解析