一、打印函数简介

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

打印函数的基本格式:

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. 我对C#的认知。

    关于开发者的技术水平到底该如何定义,到底一个人的技术水平应该定位在高.中.低的标准是什么呢?很多人觉得这是一个仁者见仁的问题,有人觉得根据公司的那个员工等级判断.答案是肯定不是,从纯开发技术的角度来分 ...

  2. kubernetes系列05—kubectl应用快速入门

    本文收录在容器技术学习系列文章总目录 1.使用kubectl 1.1 介绍 kubectl用于运行Kubernetes集群命令的管理工具. 1.2 语法 kubectl [command] [TYPE ...

  3. web缓存策略之HTTP缓存大全

    一. web缓存总分类 数据库数据缓存 Web应用,特别是SNS类型的应用,往往关系比较复杂,数据库表繁多,如果频繁进行数据库查询,很容易导致数据库不堪重荷.为了提供查询的性能,会将查询后的数据放到内 ...

  4. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

  5. Shiro源码分析之SecurityManager对象获取

    目录 SecurityManager获取过程 1.SecurityManager接口介绍 2.SecurityManager实例化时序图 3.源码分析 4.总结 @   上篇文章Shiro源码分析之获 ...

  6. frame buffer简单应用

    现在我们要在LCD上画一个点,我们无法直接对LCD屏进行操作.这时候就需要用到FrameBuffer,Linux可以FrameBuffer这个设备来供用户态进程实现直接写屏.首先我们先简单看一下lin ...

  7. [C#] 使用 StackExchange.Redis 封装属于自己的 RedisHelper

    使用 StackExchange.Redis 封装属于自己的 RedisHelper 目录 核心类 ConnectionMultiplexer 字符串(String) 哈希(Hash) 列表(List ...

  8. Android破解学习之路(十)—— 我们恋爱吧 三色绘恋 二次破解

    前言 好久没有写破解教程了(我不会告诉你我太懒了),找到一款恋爱游戏,像我这样的宅男只能玩玩恋爱游戏感觉一下恋爱的心动了.. 这款游戏免费试玩,但是后续章节得花6元钱购买,我怎么会有钱呢,而且身在吾爱 ...

  9. 【阿里云】在 Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务

     Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务 一.安装 Filezilla Server 下载最新版本的 Filezilla Server ...

  10. Android Button四种点击事件和长按事件

    项目XML代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...