嵌入式操作系统---打印函数(printf/sprintf)的实现
一、打印函数简介 作用:将“给定的内容”按照“指定的格式”输出到“指定目标内”。 打印函数的基本格式: 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++; }; } 二、打印函数的实现 、变参函数的实现(宏定义) ()va_list typedef char * va_list; va_list ap; //定义一个指针类型 ()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类型对齐; ()va_arg(ap,type) #define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) ) 指针变量ap本身指向变参列表中的下一个参数地址,va_arg取出的是当前参数的值 ()va_end(ap) #define va_end(ap) ( ap = (va_list)0 ) ap指向空 、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 = ; repeat: ++fmt;/* skip first "%" */ switch(*fmt) { case '- ' : flags |= LEFT;goto repeat; case '+ ' : flags |= PLUS;goto repeat; ... } ... ; 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 ' : ; 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 ; str = buf;//临时缓存buf首地址 end = buf + size;//临时缓存buff结束地址 if (end < buf) { end = ((); 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 (str < end) *str = '\0'; else end[-] = '\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 (str < end) *str = '\0'; else end[-] = '\0'; } return str-buf; } ()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; } }); 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; } }); 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;//参数偏移的字节数 } ()number函数 函数功能:根据类型进行数值转换 char *number(char *str, int num,int base,unsigned int flags){ ; ; ){ sign=; num=~num+; } do{ numbers[i++]=digits[do_div(num,base)]; }); if(FORMAT_BASE(flags)==FORMAT_BASE_O){ numbers[i++]='; }else if(FORMAT_BASE(flags)==FORMAT_BASE_X){ numbers[i++]='x'; numbers[i++]='; }else if(FORMAT_BASE(flags)==FORMAT_BASE_B){ numbers[i++]='b'; numbers[i++]='; } if(sign) numbers[i++]='-'; ) *str++ = numbers[i]; return str; } --------------------- 作者:huofengfeihu 来源:CSDN 原文:https://blog.csdn.net/u010961173/article/details/79769747 版权声明:本文为博主原创文章,转载请附上博文链接!
嵌入式操作系统---打印函数(printf/sprintf)的实现的更多相关文章
- C打印函数printf的一种实现原理简要分析
[0]README 0.1)本文旨在对 printf 的 某一种 实现 原理进行分析,做了解之用: 0.2) vsprintf 和 printf.c 的源码,参见 https://github.com ...
- stm32_f103使用gcc编译的环境下printf打印函数的实现
前记 gcc编译使用的printf打印函数需要的底层函数是和其他编译器不同的,以前的是无法使用的,这里有两种方法,一种是使用gcc库里面的printf函数,自己实现底层IO函数_write.另外一 ...
- 《一步一步写嵌入式操作系统》读书笔记1—Skyeye介绍、安装和HelloWorld
2013-11-14 最近在看<一步一步写嵌入式操作系统>,感觉此书甚好,许多地方讲得很清楚.可操作性强,计划边读边实践边写笔记,希望能够逐步熟悉嵌入式操作系统底层的东西,最终剪裁出一套实 ...
- PHP基础温习之echo print printf sprintf print_r var_dump的用法与区别
一.echoecho() 实际上不是一个函数,是php语句,因此您无需对其使用括号.不过,如果您希望向 echo() 传递一个以上的参数,那么使用括号会发生解析错误.而且echo是返回void的,并不 ...
- 【转】嵌入式操作系统VxWorks中TFFS文件系统的构建
时间:2005-02-20 来源:21IC中国电子网 作者:771所加固机工程部 蔡本华 高文炜 关键字:VxWorks TFFS 嵌入式操作系统 文件系统 摘要:目前的嵌入式 ...
- 浅析C语言中printf(),sprintf(),scanf(),sscanf()的用法和区别
printf语法: #include <stdio.h>int printf( const char *format, ... ); printf()函数根据format(格式)给出的格式 ...
- 一个C++版的嵌入式操作系统
原创文章,转载请注明出处! 现世面上流传着很多嵌入式操作系统,都已经非常优秀,但本人(Sam的博客-博客园)还是自己编写了一个RTOS,不敢说优秀,但绝对是使用起来最简单的.先看一个工程截图与一段m ...
- printf,sprintf,vsprintf 区别【转】
转自:http://blog.csdn.net/anye3000/article/details/6593551 有C语言写作历史的程序员往往特别喜欢printf 函数.即使可以使用更简单的命令(例如 ...
- C语言之linux内核可变参实现printf,sprintf
昨天,我发表了一篇用可变参实现的fprintf函数,其实说实话还不完全是可变参实现的,因为用到了FILE * 这样的指针,需要包含stdio.h这个头文件才能实现这个函数,今天我们就来看看,如何抛弃s ...
随机推荐
- 我对C#的认知。
关于开发者的技术水平到底该如何定义,到底一个人的技术水平应该定位在高.中.低的标准是什么呢?很多人觉得这是一个仁者见仁的问题,有人觉得根据公司的那个员工等级判断.答案是肯定不是,从纯开发技术的角度来分 ...
- kubernetes系列05—kubectl应用快速入门
本文收录在容器技术学习系列文章总目录 1.使用kubectl 1.1 介绍 kubectl用于运行Kubernetes集群命令的管理工具. 1.2 语法 kubectl [command] [TYPE ...
- web缓存策略之HTTP缓存大全
一. web缓存总分类 数据库数据缓存 Web应用,特别是SNS类型的应用,往往关系比较复杂,数据库表繁多,如果频繁进行数据库查询,很容易导致数据库不堪重荷.为了提供查询的性能,会将查询后的数据放到内 ...
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
- Shiro源码分析之SecurityManager对象获取
目录 SecurityManager获取过程 1.SecurityManager接口介绍 2.SecurityManager实例化时序图 3.源码分析 4.总结 @ 上篇文章Shiro源码分析之获 ...
- frame buffer简单应用
现在我们要在LCD上画一个点,我们无法直接对LCD屏进行操作.这时候就需要用到FrameBuffer,Linux可以FrameBuffer这个设备来供用户态进程实现直接写屏.首先我们先简单看一下lin ...
- [C#] 使用 StackExchange.Redis 封装属于自己的 RedisHelper
使用 StackExchange.Redis 封装属于自己的 RedisHelper 目录 核心类 ConnectionMultiplexer 字符串(String) 哈希(Hash) 列表(List ...
- Android破解学习之路(十)—— 我们恋爱吧 三色绘恋 二次破解
前言 好久没有写破解教程了(我不会告诉你我太懒了),找到一款恋爱游戏,像我这样的宅男只能玩玩恋爱游戏感觉一下恋爱的心动了.. 这款游戏免费试玩,但是后续章节得花6元钱购买,我怎么会有钱呢,而且身在吾爱 ...
- 【阿里云】在 Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务
Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务 一.安装 Filezilla Server 下载最新版本的 Filezilla Server ...
- Android Button四种点击事件和长按事件
项目XML代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...