一、打印函数简介

作用:将“给定的内容”按照“指定的格式”输出到“指定目标内”。

打印函数的基本格式:

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)的实现的更多相关文章

  1. C打印函数printf的一种实现原理简要分析

    [0]README 0.1)本文旨在对 printf 的 某一种 实现 原理进行分析,做了解之用: 0.2) vsprintf 和 printf.c 的源码,参见 https://github.com ...

  2. stm32_f103使用gcc编译的环境下printf打印函数的实现

    前记   gcc编译使用的printf打印函数需要的底层函数是和其他编译器不同的,以前的是无法使用的,这里有两种方法,一种是使用gcc库里面的printf函数,自己实现底层IO函数_write.另外一 ...

  3. 《一步一步写嵌入式操作系统》读书笔记1—Skyeye介绍、安装和HelloWorld

    2013-11-14 最近在看<一步一步写嵌入式操作系统>,感觉此书甚好,许多地方讲得很清楚.可操作性强,计划边读边实践边写笔记,希望能够逐步熟悉嵌入式操作系统底层的东西,最终剪裁出一套实 ...

  4. PHP基础温习之echo print printf sprintf print_r var_dump的用法与区别

    一.echoecho() 实际上不是一个函数,是php语句,因此您无需对其使用括号.不过,如果您希望向 echo() 传递一个以上的参数,那么使用括号会发生解析错误.而且echo是返回void的,并不 ...

  5. 【转】嵌入式操作系统VxWorks中TFFS文件系统的构建

    时间:2005-02-20 来源:21IC中国电子网 作者:771所加固机工程部 蔡本华 高文炜 关键字:VxWorks   TFFS   嵌入式操作系统   文件系统       摘要:目前的嵌入式 ...

  6. 浅析C语言中printf(),sprintf(),scanf(),sscanf()的用法和区别

    printf语法: #include <stdio.h>int printf( const char *format, ... ); printf()函数根据format(格式)给出的格式 ...

  7. 一个C++版的嵌入式操作系统

     原创文章,转载请注明出处! 现世面上流传着很多嵌入式操作系统,都已经非常优秀,但本人(Sam的博客-博客园)还是自己编写了一个RTOS,不敢说优秀,但绝对是使用起来最简单的.先看一个工程截图与一段m ...

  8. printf,sprintf,vsprintf 区别【转】

    转自:http://blog.csdn.net/anye3000/article/details/6593551 有C语言写作历史的程序员往往特别喜欢printf 函数.即使可以使用更简单的命令(例如 ...

  9. C语言之linux内核可变参实现printf,sprintf

    昨天,我发表了一篇用可变参实现的fprintf函数,其实说实话还不完全是可变参实现的,因为用到了FILE * 这样的指针,需要包含stdio.h这个头文件才能实现这个函数,今天我们就来看看,如何抛弃s ...

随机推荐

  1. RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2-新增锁定用户与解除锁定用户的功能

    锁定用户功能在现实应用场景中得到了大量的应用,当我们需要限制某用户的登录,又不能删除这个用户时就可以使用锁定功能,如:未授权的用户尝试错误密码错误过多可以尝试的用户进行锁定,又如ATM机上取钱时密码错 ...

  2. Node.js 进程平滑离场剖析

    本文由云+社区发表 作者:草小灰 使用 Node.js 搭建 HTTP Server 已是司空见惯的事.在生产环境中,Node 进程平滑重启直接关系到服务的可靠性,它的重要性不容我们忽视.既然是平滑重 ...

  3. 线程组ThreadGroup分析详解 多线程中篇(三)

    线程组,顾名思义,就是线程的组,逻辑类似项目组,用于管理项目成员,线程组就是用来管理线程. 每个线程都会有一个线程组,如果没有设置将会有些默认的初始化设置 而在java中线程组则是使用类ThreadG ...

  4. Nginx的负载均衡

    什么是负载均衡 负载均衡主要通过专门的硬件设备或者通过软件算法实现.通过硬件设备实现的负载均衡效果好.效率高.性能稳定,但是成本比较高.通过软件实现的负载均衡主要依赖于均衡算法的选择和程序的健壮性.均 ...

  5. Spring拓展接口之BeanFactoryPostProcessor,占位符与敏感信息解密原理

    前言 开心一刻 一只被二哈带偏了的柴犬,我只想弄死隔壁的二哈 what:是什么 BeanFactoryPostProcessor接口很简单,只包含一个方法 /** * 通过BeanFactoryPos ...

  6. Aooms_微服务基础开发平台实战_003_配置文件与简单的web环境搭建

    一.前言 本篇文章介绍两个重点 (1) 工程核心配置文件application.yml (2) 如何在一个标准的的SpringCloud工程上构建起一个基本的web结构 二.配置文件applicati ...

  7. web前端图片上传

    图片上传有很多种形式,但是听说ios只能传字符串,所以为了安卓.ios和web能用一个接口上传图片,采用了基于base64 的方法上传图片. 下面是我的html <div class=" ...

  8. 使用String. localeCompare比较字符串

    javascript提供stringA.localeCompare(stringB)方法,来判断一个字符串stringB是否排在stringA的前面. 返回值:    如果引用字符存在于比较字符之前则 ...

  9. 【Dojo 1.x】笔记1 入门

    Dojo是个框架 ,是个js框架,现在除了这一点什么都不知道,就这么上手了. ps:不建议Web初学者看我的笔记,这个要有一定积累才能看. 在<body>标签内写这么一点代码: <b ...

  10. 从.Net到Java学习第三篇——spring boot+mybatis+mysql

    从.Net到Java学习第一篇——开篇 环境:mysql5.7 新建mysql数据库demo,然后执行如下sql脚本进行数据表创建和数据初始化: -- ------------------------ ...