c语言重定向printf函数,printf函数的重定向实现

2023-05-16

printf函数的重定向实现

(2014-09-05 13:50:29)

标签:

佛学

在嵌入式系统中,串口常用来辅助调试输出一些调试信息。所以有必要实现串口的格式化输出功能,这可以由3种方法实现(就我目所知).

1)使用系统库函数printf(),这就需要重载输入,输出函数int fputc(int ch, FILE *f);int fgetc(FILE *f).

2)使用sprintf()函数将数据格式化到数组,然后将数组输出.也可以使用vsprintf().

3)自己写类似printf()函数.

这里假设已经编写了基本的输入输出函数如下:

int sendchar(int ch); // 发送字符

int sendstr(char *str);//发送字符窜

int getkey(void); // 接受字符

1)第一种方法的实现

比较简单只要实现以下2个函数:

int fputc(int ch, FILE *f) {

return (sendchar(ch));

}

int fgetc(FILE *f) {

return (getkey());

}

使用方法:

#include

void main()

{

...

printf("%s,%d,%x","hello",0x10,0x10);//输出: hello,16,10

...

}

2)第二种方法的实现

void Uart_Printf(const char *fmt,...)

{

va_list ap;

char string[256];

va_start(ap,fmt);

vsprintf(string,fmt,ap);

Uart_SendString(string);

va_end(ap);

}

3)第三种方法的实现

int myprintf (const char* str,...)

{

va_list arp;

uint8 c, f, r;

ULONG val;

char s[16];

int i, w, res, cc;

va_start(arp, str);

for (cc = res = 0; cc != EOF; res += cc) {

c = *str++;

if (c == 0) break;

if (c != '%') {

sendchar(c);

cc=1; //cc保存当前循环发送的数据

continue;

}

w = f = 0; //f为格式 ,w为宽度

c = *str++;

if (c == '0') {

f = 1;

c = *str++; //等于c = *(str++);先取值,最后str++ ;f的第0位代表用0填充对齐 c = *(str++);

}

while (c >= '0' && c <= '9') {

w = w * 10 + (c - '0'); //计算对齐宽度

c = *str++;

}

if (c == 'l' || c=='L') {

f |= 2; c = *str++; //f的第二位代表Long前缀

}

if (c == 's' || c == 'S') {

cc = sendstr(va_arg(arp, char*));//发送字符窜,cc=放松字符个数

continue;

}

if (c == 'c' || c == 'C') {

sendchar(va_arg(arp, int)); //char 改 int

cc = 1;

continue;

}

r = 0;

if (c == 'd' || c =='D') r = 10; //r代表进制

if (c == 'u' || c == 'U') r = 10;

if (c == 'X' || c == 'x') r = 16;

if (r == 0) break;

if (f & 2) {

val = (ULONG)va_arg(arp, long); //获取Long型参数,arp指向下一个参数

} else { //没有'l'标志

val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);

//不管是int 还是 unsigned int 都转化为 unsigned long

}

if (c == 'd') {//如果是'd'前缀就要判断刚获取的参数是正还是付

if (val >= 0x80000000) { //最高位为1说明是负的

val = 0 - val;

f |= 4;//f的第三位代表"+/-"

}

}

i = sizeof(s) - 1; s[i] = 0;//i=15

do {

c = (uint8)(val % r + '0');//r代表进制

if (c > '9') c += 7; //对于16进制 转换到对应的'abc...'需要+7 :'\58'+7='65'='a'

s[--i] = c; //从后面开始保存

val /= r;

} while (i && val);

if (i && (f & 4)) s[--i] = '-';//是负数添加'-'号

w = sizeof(s) - 1 - w;

while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';

cc = sendstr(&s[i]);

}

va_end(arp);

return (cc == EOF) ? cc : res;

}

变参数表的调用形式以及原理:

参数在内存中存放格式:大多数情况堆栈是从高到低生长,函数参数入栈时后面的参数先入栈。参数弹出顺序与入栈顺序相反。

举个例子如下:

void func(int x, float y, char z);

那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

下面是MDK 里面重要的几个宏定义如下:

typedef struct __va_list { void *__ap; } va_list;

#define va_arg(ap, type) __va_arg(ap, type)

#define va_end(ap) ((void)(ap))

使用方法:

在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);

然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;

然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;

获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。

va_list的"等价"替换:

va_list arp; <==> {char *arp = 0;}

va_start(arp, str);<==>{ arp = (char *)&str; arp += sizeof(str); }

sendstr(va_arg(arp, type));<==> { sendstr( *(type*)arp); arp += sizeof(type);}

va_end(arp); <==>{ arp = 0;}

因此上述函数可以改为:

int myprintf ( const char* str, ...)

{

//va_list arp;

char *arp = 0;

uint8 c, f, r;

ULONG val;

char s[16];

int i, w, res, cc;

//va_start(arp, str);

arp = (char *)&str;

arp += sizeof(str);

for (cc = res = 0; cc != EOF; res += cc) {

c = *str++;

if (c == 0) break;

if (c != '%') {

sendchar(c);

cc=1; //cc保存当前循环发送的数据

continue;

}

w = f = 0; //f为格式 ,w为宽度

c = *str++;

if (c == '0') {

f = 1;

c = *str++; //等于c = *(str++);先取值,最后str++ ;f的第0位代表用0填充对齐 c = *(str++);

}

while (c >= '0' && c <= '9') {

w = w * 10 + (c - '0'); //计算对齐宽度

c = *str++;

}

if (c == 'l' || c=='L') {

f |= 2; c = *str++; //f的第二位代表Long前缀

}

if (c == 's' || c == 'S') {

//cc = sendstr(va_arg(arp, char*));//发送字符窜,cc=放松字符个数

sendstr( *(char **)arp);

arp += sizeof(char *);

continue;

}

if (c == 'c' || c == 'C') {

// sendchar(va_arg(arp, int)); //char 改 int

sendchar(*((int *)arp));

arp += sizeof(int);

cc = 1;

continue;

}

r = 0;

if (c == 'd' || c =='D') r = 10; //r代表进制

if (c == 'u' || c == 'U') r = 10;

if (c == 'X' || c == 'x') r = 16;

if (r == 0) break;

if (f & 2) {

//val = (ULONG)va_arg(arp, long); //获取Long型参数,arp指向下一个参数

val=(ULONG)(*((long *)arp));

arp += sizeof(ULONG);

} else { //没有'l'标志

val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);

//不管是int 还是 unsigned int 都转化为 unsigned long

}

if (c == 'd') {//如果是'd'前缀就要判断刚获取的参数是正还是付

if (val >= 0x80000000) { //最高位为1说明是负的

val = 0 - val;

f |= 4;//f的第三位代表"+/-"

}

}

i = sizeof(s) - 1; s[i] = 0;//i=15

do {

c = (uint8)(val % r + '0');//r代表进制

if (c > '9') c += 7; //对于16进制 转换到对应的'abc...'需要+7 :'\58'+7='65'='a'

s[--i] = c; //从后面开始保存

val /= r;

} while (i && val);

if (i && (f & 4)) s[--i] = '-';//是负数添加'-'号

w = sizeof(s) - 1 - w;

while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';

cc = sendstr(&s[i]);

}

//va_end(arp);

arp = 0;

return (cc == EOF) ? cc : res;

}

分享:

a4c26d1e5885305701be709a3d33442f.png喜欢

0

a4c26d1e5885305701be709a3d33442f.png赠金笔

加载中,请稍候......

评论加载中,请稍候...

发评论

登录名: 密码: 找回密码 注册记住登录状态

昵   称:

评论并转载此博文

a4c26d1e5885305701be709a3d33442f.png

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c语言重定向printf函数,printf函数的重定向实现 的相关文章

  • bp神经网络如何用simulink实现

    BP神经网络可以使用Simulink中的神经网络工具箱来实现 首先 xff0c 需要在Simulink中打开神经网络工具箱 xff0c 然后添加一个多层前馈神经网络模型 接着 xff0c 可以使用工具箱中的各种组件 xff0c 如输入层 隐
  • 在 GitLab 中自动统计代码行数

    在 GitLab 中 xff0c 你可以在项目的 34 Settings 34 页面中找到 34 Repository statistics 34 选项 点击 34 Repository statistics 34 后 xff0c 你就可以
  • 写一段0-100 单数累加的代码

    以下是一段 Python 代码 xff0c 用于计算0 100之间的所有单数的总和 xff1a total 61 span class hljs number 0 span span class hljs keyword for span
  • 写一段python保存matplotlib图片的示例

    示例代码 xff1a import matplotlib pyplot as plt 创建图片 plt plot 1 2 3 4 保存图片 plt savefig 39 my picture png 39
  • Python的快速排序怎么写

    快速排序是一种分治的算法 xff0c 它的基本思想是 xff1a 通过一趟排序将要排序的数据分割成独立的两部分 xff0c 其中一部分的所有数据都比另外一部分的所有数据都要小 xff0c 然后再按此方法对这两部分数据分别进行快速排序 xff
  • fragment的管理

    private void initData fragmentManager 61 getSupportFragmentManager fragment1 61 new Fragment1 fragment2 61 new Fragment2
  • 使用libcurl 建立长连接

    libcurl 是一个用于网络传输的开源库 xff0c 它支持多种协议 xff0c 包括 HTTP 要使用 libcurl 建立长连接 xff0c 需要设置 CURLOPT CONNECT ONLY 选项 这将告诉 libcurl 只建立连
  • C++ char* 转 char[] 给出示例代码

    char 转 char 的示例代码如下 xff1a span class hljs meta span class hljs keyword include span span class hljs string lt iostream g
  • socket的select的用法

    select 是一个用于在多个套接字上进行非阻塞 I O 操作的函数 它可以同时监视多个套接字 xff0c 并在有数据可读 可写或有错误发生时返回 该函数的语法如下 xff1a span class hljs function span c
  • python爬虫登录网页

    爬虫登录网页是指使用爬虫程序自动模拟登录流程 获取网站的数据 在 Python 中登录网页 需要使用第三方库如 requests mechanicalsoup 或者 selenium 等来实现 具体实现方法可以参考以下步骤 导入所需库 如
  • 只能选择分卷文件的第1部分_科普一下:天下无优分卷压缩附件的解压方法

    天下无优网优资料分享平台 https netopt net 处于性能考虑 xff0c 对所有上传的附件进行了10MB的附件大小限制 如果要分享超过10MB的文件 xff0c 我们推荐了使用 分卷压缩 的方式将一个大文件拆分并压缩成多个10M
  • 计算机存储器组成结构,计算机组成原理——存储器内部组成

    存储器的基本组成 这里讨论的是主存 xff0c 即内存 存储器 组成结构图 JPG 主存储器包括存储体 MAR MDR 各种逻辑部件及控制电路 存储器内部结构说明 一个主存储器 内存条 由多个存储体组成 xff0c 一个存储体中有许多存储单
  • doris和hive的区别

    Doris和Hive是两种开源的数据仓库工具 xff0c 都可以用来分析大型数据集 Doris是由阿里巴巴开发的一款大数据分析工具 xff0c 具有低延迟 高吞吐 高可扩展性等优点 它采用PAL Parallel amp Analytic
  • 如何安装chatgpt

    首先 xff0c 你需要确保你的计算机安装了 Python 环境 如果没有安装 xff0c 请参考以下网址进行安装 xff1a https www python org downloads 然后 xff0c 打开命令行窗口 在 Window
  • AttributeError: module 'numpy' has no attribute 'int'

    这个错误消息表明 xff0c 你试图访问 numpy 模块的 int 属性 xff0c 但是 numpy 模块中没有这个属性 这通常是因为你的代码中有一个语法错误 xff0c 导致 Python 解释器识别错误 具体来说 xff0c 可能是
  • listview多条目加载

    public class MyBaseAdapter extends BaseAdapter private Context context private ArrayList lt JsonBean DataBean Liketeamli
  • tensorboard怎么使用

    TensorBoard 是一个由 TensorFlow 提供的可视化工具 xff0c 可以帮助用户更好地理解和调试深度学习模型 使用 TensorBoard xff0c 您可以可视化训练过程中的损失和指标 模型结构 梯度直方图 嵌入向量等等
  • 写一段java主动发送请求的示例代码

    String url 61 34 http example com example php 34 try URL obj 61 new URL url HttpURLConnection con 61 HttpURLConnection o
  • idea chatGPT 插件 如何使用

    IDEA ChatGPT 插件是一个使用 OpenAI 的 GPT 3 模型的插件 xff0c 它能够帮助您生成代码 xff0c 提供代码自动完成和错误修复功能 要使用该插件 xff0c 您需要先在 IDEA 中安装插件 您可以在 IDEA
  • mybatisplus使用LambdaQueryWrapper查询时怎么将查询结果中所有的id放入数组中

    可以使用LambdaQueryWrapper的listObjs方法来查询数据库中所有符合条件的记录的某一个字段 xff0c 并将这些字段值放入一个数组中 具体实现方法如下 xff1a span class hljs built in Lis

随机推荐