1、聊一聊

今晚平安夜,记得一定要吃苹果哦!同时提前祝大家圣诞快乐,Merry Christmas!

是一首超级有氛围感的圣诞歌曲,大伙可以听一听,请相信我的品位!

2、正文部分

对于printf相信不用我过多介绍,它算是初学C语言时用得最多的信息输出接口函数了,对于玩MCU、Linux等嵌入式的朋友,基本上都会用其进行串口日志打印。printf是将字符串到标准输出stdout,比如标准输出是屏幕、串口终端等等,由于用户需求不一样都会进行输出的重定向,从而打印信息到想要的输出设备上。而今天的主角sprintf是字符串格式化命令,与printf相比我觉得其更多的是一个转换函数,类似于大写转化为小写。sprintf可以按照用户定义的格式转为对应的字符串并填充到缓冲buff中。所以printf其实相当于" sprintf+把字符串输出到标准输出 ",有些平台printf函数其实就是这两种的封装。因此printf的重定向就可以认为仅仅只是改变了标准的输出接口。不知曾几何时,接触到了sprintf以后,在实际的项目中就很少再去使用printf,因为sprintf的强大完全可以替代printf,并且在有些应用中显得非常灵活。

1

体验sprintf

首先了解一下sprintf :Fuction :int sprintf(char *string, char *format [,argument,...]);Param1 : 最终格式化字符串所存储的buff。Param2 : 可变参数,类似于printf中的”%d”格式。return 最终打印到字符缓冲区中的字符数目,结束字符‘\0’不计入内。

参考示例:

#include char strBuff[40] ={'\n'};/******************************************** * Fuction : UartString * Descri  : 进行sprintfDemo演示  * Author  : bug菌  *******************************************/ void UartString(char * strBuff) {   //模拟串口输出  printf("%s\r\n",strBuff);}/******************************************** * Fuction : main * Descri  : 进行sprintfDemo演示  * Author  : bug菌  *******************************************/ int main(void){int strIndex = 0;//打印整形  sprintf(strBuff,"bugNum = %d",1000); UartString(strBuff); //打印浮点  sprintf(strBuff,"PI = %.4f",3.1415926); UartString(strBuff); //打印地址 sprintf(strBuff,"address = %p",&strIndex); UartString(strBuff); //字符串拼接 strIndex = sprintf(strBuff,"PI = %.4f",3.1415926);  strIndex = sprintf(strBuff+strIndex,"926"); UartString(strBuff); //简单输出  strIndex = sprintf(strBuff,"欢迎大家关注公众号:最后一个bug");  UartString(strBuff); return 0;}

结果输出:

说明一下:

从上面的演示了解到printf与sprintf仅仅只是其前面增加了一个buff缓冲,其可变参数格式部分用法与printf几乎相同。

而且通过利用sprintf返回值还可以方便、灵活的进行多个字符串的拼接, 相比strcat进行两个字符串拼接确实要方便多了。

2

sprintf注意事项

1

buff溢出

sprintf最大的问题是容易缓存区溢出,一旦可变部分所拼接的字符串长度超过buff的大小,便会造成数据溢出,从而危及程序运行。特别是在进行浮点操作的时候尤为要注意,比如把上面的Demo浮点数打印不进行格式处理。

 //打印浮点  //sprintf(strBuff,"PI = %.4f",3.14); sprintf(strBuff,"PI = %f",3.14); UartString(strBuff); 

相比预期的输出3.14 后面增加了0000,如果strbuff定义的过小就会导致数据溢出。

2

snprintf

由于使用sprintf开发者容易导致缓冲区溢出,然而这样的bug有时候隐藏得比较深,导致难以排查,所以就有了一个安全性稍微高一点的snprintf函数,该函数在sprintf函数的基础上增加了一个缓冲区长度的参数,通过该参数函数内部用来避免sprintf()存在的溢出风险。

Fuction :int snprintf(char *str, size_t size, const char *format, ...);

Param1 最终格式化字符串所存储的buff。

Param2 buff缓存区的长度。

Param3 : 可变参数,类似于printf中的”%d”格式。

return : 若成功则返回预写入的字符串长度,若出错则返回负数。

参考示例:

#include #define BUFF_SIZE 11char strBuff[BUFF_SIZE] ={'\n'};/******************************************** * Fuction : UartString * Descri  : 进行sprintfDemo演示  * Author  : bug菌  *******************************************/ void UartString(char * strBuff) {   //模拟串口输出  printf("%s\r\n",strBuff);}/******************************************** * Fuction : main * Descri  : 进行snprintfDemo演示  * Author  : bug菌  *******************************************/ int main(void){int strIndex = 0;//打印浮点  printf("return :%d\n",snprintf(strBuff,BUFF_SIZE,"PI = %f\r\n",3.14)) ; UartString(strBuff); return 0;}

输出结果:

说明一下:

注意一点snprintf返回的是预写字符串长度,而非最终写入到strbuff中的字符个数,当然当缓存区足够的时候预写长度=最终写入到strbuff中的字符串长度。

3

简化版snprintf

一个功能全面的printf、sprintf、snprintf等等都会有较大的代码量,同样标准C库的也是一样的,对于一些资源比较紧张的MCU等可能一个标准函数就占用了一大半的Flash等ROM区,一个库函数实现比主体代码还耗资源,这样在嵌入式中是不应该的。

然而,对于这些printf函数其实我们并不需要其全部的功能,可能只需要个打印整形、浮点等数据即可满足需求,其他格式的代码实现完全可以去除,从而可以大大缩减其占用的ROM资源,所以就有了精简版的snprintf,对于snprintf可以在标准库源码上进行相关功能的删减即可,bug菌在这里就不过多介绍了,网络上资源也一大把。

3、结束语

今天的知识就跟大家分享到这里,sprintf还有很多巧妙的格式等你去挖掘,相信这是一段美妙的学习之旅!好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地,觉得不错可以给bug菌点个赞。

推荐好文  点击蓝色字体即可跳转

☞【经验】bug菌谈单片机编程"十层功力",你练到了第几层?

☞【MCU】可怕,别人把我MCU固件给反汇编了!(逆向)

☞ 【神器】这三款网络抓包工具在手,同事想甩锅都难!

☞ 【编码】This code is garbage!

c++ 缓存和缓冲_【嵌入式C】放弃printf,选择了精简snprintf相关推荐

  1. 做柜员还是程序员_程序员放弃互联网选择去银行,但想到同学工资高好几倍心里不平衡...

    原标题:程序员放弃互联网选择去银行,但想到同学工资高好几倍心里不平衡 一名程序员在论坛上表达了自己内心的苦恼与失落:放弃了一线的互联网大厂去银行做技术,以后还怎么平衡身边同学工资是自己几倍的心理. 这 ...

  2. fiq中断的入口地址_嵌入式技巧:ARM的三种中断调试方法介绍

    嵌入式软件开发流程 参照嵌入式软件的开发流程.第一步:工程建立和配置.第二步:编辑源文件.第三步:工程编译和链接.第四步:软件的调试.第五步:执行文件的固化. 在整个流程中,用户首先需要建立工程并对工 ...

  3. 缓存与缓冲的区别 Difference Between Cache and Buffer

    2019独角兽企业重金招聘Python工程师标准>>> Cache vs Buffer Both cache and buffer are temporary storage are ...

  4. 嵌入式与人工智能关系_嵌入式人工智能的发展趋势

    嵌入式与人工智能关系_嵌入式人工智能的发展趋势 所谓嵌入式人工智能,就是设备无须联网通过云端数据中心进行大规模计算去实现人工智能,而是在本地计算,在不联网的情况下就可以做实时的环境感知.人机交互.决策 ...

  5. 白话讲解缓存和缓冲区别

    缓存和缓冲,两者都是将数据暂存在一个容器中.那两者怎么区分呢?下面我已生活例子打一个举例. 缓冲:我们倒垃圾的时候,不可能一有垃圾就直接跑去垃圾站倒垃圾吧.而是先把垃圾倒到垃圾桶,垃圾桶满了再一起倒到 ...

  6. 嵌入式工程师该如何选择合适的开发系统

    在进入正题之前,需要先理解"嵌入"的概念,在工程师来看主要从以下3个方面入手. 1.从硬件上,将基于CPU的处围器件整合到CPU芯片内部,比如早期基于X86体系结构下的计算机,CP ...

  7. Linux嵌入式学习——c语言选择结构设计

    Linux嵌入式学习--c语言选择结构设计 一.if语句 1.1if语句的一般格式 1.2if语句常用的3种形式 1.3if语句的嵌套 二.关系运算符和关系表达式 2.1关系运算符及其优先次序 2.2 ...

  8. python打蛇_页高速缓存(下)_Linux系统编程第 3 期:文件I/O缓存与内存映射视频课程_嵌入式开发视频-51CTO学院...

    <嵌入式工程师自我修养>自学系列教程 一线嵌入式工程师精心打造,嵌入式学习路线六步走: 第 1 步:Linux三剑客(更新完毕)零基础玩转Linux+Ubuntu Git零基础实战:Lin ...

  9. windows kernel 可以直接读写文件系统资料吗_嵌入式杂谈之文件系统

    作者:于宙 , 经授权转载:编辑略作调整: 文件系统可以说是嵌入式中绕不开的一大块内容.之前我对文件系统认知一直停留在在U盘格式的理解上,直到接触了嵌入式Linux才发现这里面大有文章,以Linux启 ...

最新文章

  1. 深入浅出数据仓库中SQL性能优化之Hive篇
  2. python 任务计时器 apscheduler.schedulers
  3. Windows7 Search Federation功能解读
  4. geometry-api-java 学习笔记(二)点 Point
  5. parallels desktop虚拟机与Mac共享网络设置方法
  6. 老师,你和我的文具撞衫了!
  7. 原码一位乘法器设计_十分钟带你彻底搞懂原码、反码、补码
  8. CVTE【嵌入式应用开发】【软件技术支持】面经【已拿offer】
  9. 基于KVM、Xen、OpenVZ等虚拟化技术的WEB在线管理工具
  10. 数据结构笔记(七)-- 顺序栈
  11. dropout层加在哪里_神经网络Dropout层中为什么dropout后还需要进行rescale?
  12. 中缀表达式变为后缀表达式
  13. layui 小卡片_如何评价LayUI和他的作者贤心?
  14. mysql数据库设计与应用答案智慧树_知到智慧树MySQL数据库设计与应用完整免费答案...
  15. Mybatis一发入魂
  16. Python_03序列、函数
  17. activiti学习之排他网关
  18. 华为AREngine根据深度图获取深度信息
  19. diskgenius分区教程(diskgenius分区教程)
  20. Symbiosis(共生)和Commensalism(共栖)的区别

热门文章

  1. c#如何读取相机手机的拍摄时间
  2. Google面试题及答案
  3. 第一章 用户及组的管理
  4. 现代软件工程 团队博客作业 如何评价个人在团队中的绩效
  5. 在交换机上,广播帧是如何发送的,
  6. 对网络中安全审计产品的理解
  7. 图的顺序存储结构及C语言实现
  8. go 读取 json 配置文件
  9. 软件测试2019:第四次作业
  10. 撕起来了!谁说数据少就不能用深度学习?这锅俺不背!