0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于你可能不知道的printf

黄工的嵌入式技术圈 来源:黄工的嵌入式技术圈 作者:黄工的嵌入式技术 2020-02-05 12:28 次阅读

前言

printf可能是我们在学习C语言的过程中最早接触的库函数了。其基本使用想必我们都已经非常清楚了。但是下面的这些情况你是否已经清楚地知道了呢?

示例程序

我们来看一个示例程序,看看你能否对下面的结果输出有非常清晰的认识。

#include intmain(void) { inta=4; intb=3; intc=a/b; floatd=*(float*)(&c); longlonge=0xffffffffffffffff; printf("a/b:%f,a:%d\n",a/b,a,b);//打印0 printf("(float)a/b:%f\n",((float)a)/b);//打印1 printf("(double)a/b:%lf\n",((double)a)/b);//打印2 printf("d:%f\n",d);//打印3 printf("%.*f\n",20,(double)a/b);//打印4 printf("e:%d,a:%d\n",e,a);//打印5 printf("a:%d,++a:%d,a++:%d\n",a,++a,a++);//打印6 return0; }

编译为32位程序:

gcc-m32-otesttest.c

在运行之前,你可以自己先猜想一下打印结果会是什么。实际运行结果:

a/b:0.000000,a:3//打印0的结果 (float)a/b:1.333333//打印1的结果 (double)a/b:1.333333//打印2的结果 d:0.000000//打印3的结果 1.33333333333333325932//打印4的结果 e:-1,a:-1//打印5的结果 a:6,++a:6,a++:4//打印6的结果

你的猜想是否都正确呢?如果猜想错误,那么接下来的内容你就不应该错过了。

你是否会有以下疑问:

0.打印0的a/b为什么不是1,a为什么不是4?

1.打印1和打印2有什么区别呢?

2.打印3为什么结果会是0.000000?

3.打印4的结果为什么最后的小数位不对?其中的*是什么意思?

4.打印5中,为什么a的值是-1而不是4?

5.打印6中,结果为什么分别是6,6,4?

在解答这些问题之前,我们需要先了解一些基本内容。

可变参数中的类型提升

printf是接受变长参数的函数,传入printf中的参数个数可以不定。而我们在变长参数探究中说到:
调用者会对每个参数执行“默认实际参数提升",提升规则如下:
——float将提升到double
——char、short和相应的signed、unsigned类型将提升到int

也就是说printf实际上只会接受到double,int,long int等类型的参数。而从来不会实际接受到float,char,short等类型参数。
我们可以通过一个示例程序来检验:

//badcode #include intmain(void) { char*p=NULL; printf("%d,%f,%c\n",p,p,p); return0; }

编译报错如下:

printf.c:Infunction‘main’: printf.c:5:12:warning:format‘%d’expectsargumentoftype‘int’,butargument2hastype‘char*’[-Wformat=] printf("%d,%f,%c\n",p,p,p); ^ printf.c:5:12:warning:format‘%f’expectsargumentoftype‘double’,butargument3hastype‘char*’[-Wformat=] printf.c:5:12:warning:format‘%c’expectsargumentoftype‘int’,butargument4hastype‘char*’[-Wformat=]

我们可以从报错信息中看到:

%d 期望的是 int 类型参数

%f 期望的是 double 类型参数

%c 期望的也是 int 类型参数

而编译之所以有警告是因为,char *类型无法通过默认实际参数提升,将其提升为int或double。

参数入栈顺序以及计算顺序

在C语言中,参数入栈顺序是确定的,从右往左。而参数的计算顺序却是没有规定的。也就是说,编译器可以实现从右往左计算,也可以实现从左往右计算。

浮点数的有效位

对于double类型,其有效位为15~~16位(参考:对浮点数的一些理解)。

可变域宽和精度

printf中,*的使用可实现可变域宽和精度,使用时只需要用*替换域宽修饰符和精度修饰符即可。在这样的情况下,printf会从参数列表中取用实际值作为域宽或者精度。示例程序如下:

#include intmain(void) { floata=1.33333333; char*p="hello"; printf("%.*f\n",6,a); printf("%*s\n",8,p); return0; }

运行结果:

1.333333 hello

而这里的6或者8完全可以是一个宏定义或者变量,从而做到了动态地格式控制。

格式控制符是如何处理参数的

printf有很多格式控制符,例如%d,它在处理输入时,会从堆栈中取其对应大小,即4个字节作为对应的参数值。也就是说,当你传入参数和格式控制符匹配或者在经过类型提升后和格式控制符匹配的时候,参数处理是没有任何问题的。但是不匹配时,可能会出现未定义行为(有两种情况例外,我们后面再说)。例如,%f期望一个double(8字节)类型,但是传入的参数是int(4字节),那么在处理这个int参数值,可能会多处理4个字节,并且也会造成处理数据错误。

真相大白

有了前面这些内容的铺垫,我们再来解答开始的疑问:

对于问题0,a/b的结果显然为4字节的int类型1,而%f期望的是8字节的double,而计算结果只有4个字节,因此会继续格式化后面4个字节的a,而整型1和后面a组合成的8字节数据,按照浮点数的方式解释时,它的值就是0.000000了。由于前面已经读取解释了a的内容,因此第二个%d只能继续读取4个字节,也就是b的值3,最终就会出现打印a的值是3,而不是4。

对于问题1,实际上在printf中,是不需要%lf的,%f期望的就是double类型,在编译最开始的示例程序其实就可以发现这个事实。当然了在scanf函数中,这两者是有区别的。

对于问题2,也很简单,2的二进制存储形式按照浮点数方式解释读取时,就是该值。

对于问题3,double的有效位为15~16位,也就是之外的位数都是不可靠的。printf中的*可用于实现可变域宽和精度,前面已经解释过了。

对于问题4,这里不给出,留给读者思考,欢迎大家可留言区给出原因。

对于问题5,虽然参数计算顺序没有规定,但是实际上至少对于gcc来说,它是从右往左计算的。也就是说,先计算a++,而a++是先用在加,即压入a=4,其后,a的值变为5;再计算++a,先加再用,即压入a=5+1=6;最后a=6,压入栈。最终从左往右压入栈的值就分别为6,6,4。也就是最终的打印结果。但是实际情况中,这样的代码绝对不该出现!

至此,真相大白。

总结

虽然我们前面解释了那些难以理解的现象,同时读者可以参考变长参数探究和对浮点数的一些理解找到更多的信息。但是我们在实际编程中应该注意以下几点:

格式控制符应该与对应参数类型匹配或者与类型提升后的参数类型匹配。

绝对避免出现计算结果与参数计算顺序有关的代码。

*在printf中实现可变域宽和精度。

printf不会实际接受到char,short和float类型参数。

如果%s对应的参数可能为NULL或者对应整型,那将是一场灾难。

不要忽略编译器的任何警告,除非你很清楚你在做什么。

例外情况指的是有符号整型和无符号整型之间,以及void*和char*之间。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 打印
    +关注

    关注

    1

    文章

    64

    浏览量

    18711
  • 程序
    +关注

    关注

    116

    文章

    3779

    浏览量

    80894
  • Printf
    +关注

    关注

    0

    文章

    82

    浏览量

    13628
收藏 人收藏

    评论

    相关推荐

    关于陶瓷电路板不知道的事

    陶瓷电路板(Ceramic Circuit Board),又称陶瓷基板,是一种以陶瓷材料为基体,通过精密的制造工艺在表面形成电路图形的高技术产品,快来看看哪些是您还不知道的事?
    的头像 发表于 10-21 11:55 206次阅读
    <b class='flag-5'>关于</b>陶瓷电路板<b class='flag-5'>你</b><b class='flag-5'>不知道</b>的事

    又一电工不知道,施耐德变频器怎么复位,如果不告诉知道怎么复位吗?

    维修 我给他说,可能以前没接触过施耐德的变频器,就不知道它还有一个盖子,要复位的话,就要把盖子打开,第一次找不到也正常,要是经常断电重启的话,对设备不好。 变频器修理 之所以分享给大家,就是想告诉那些没接触过施耐
    的头像 发表于 10-12 15:15 199次阅读
    又一电工<b class='flag-5'>不知道</b>,施耐德变频器怎么复位,如果不告诉<b class='flag-5'>你</b>,<b class='flag-5'>你</b><b class='flag-5'>知道</b>怎么复位吗?

    使用CS1232 采集一个小信号,信号有可能是正,也可能是负 ,是不知道怎么回事情?

    我使用CS1232 采集一个小信号,信号有可能是正,也可能是负 但是不知道怎么回事情,采集的数据就经常不对 在上图的电路中,当我把P4 短路,采集到的电压尽然是为8388608 我看了一下SDO
    发表于 09-25 14:36

    知道贴片电感故障时可能出现的症状吗?

    知道贴片电感故障时可能出现的症状吗?
    的头像 发表于 08-17 14:20 323次阅读
    <b class='flag-5'>你</b><b class='flag-5'>知道</b>贴片电感故障时<b class='flag-5'>可能</b>出现的症状吗?

    新手尝试做一个LED驱动电路,不知道电路有没有问题

    TP8006稳流驱动,最后留出LED插口。 因为不是很懂硬件设计,不知道做的模块能不能行,很希望各位提提意见,让我认识认识硬件设计上的缺陷。
    发表于 07-24 18:35

    这些不知道的卧式共模电感的选型依据

    这些不知道的卧式共模电感的选型依据 gujing 编辑:谷景电子 实际上关于卧式共模电感的选型已经有讨论过很多这方面的内容,但一直都还有人在讨论这些问题。本篇我们换几个角度来探讨一下卧式共模电感
    的头像 发表于 04-29 22:42 341次阅读

    关于静电放电不知道的知识

    在整个半导体制造过程中,微粒污染、静电放电损坏以及与此相关联的设备停机,是静电带来的三大问题。
    的头像 发表于 03-27 11:12 633次阅读

    辊压机轴承位磨损修复不知道的那些事

    电子发烧友网站提供《辊压机轴承位磨损修复不知道的那些事.docx》资料免费下载
    发表于 03-12 15:10 0次下载

    吸尘器究竟是如何替“吃灰”的【其利天下技术】

    如今,吸尘器已成为大多数人居家必备的小家电产品,那么说起吸尘器,对吸尘器有了解多少呢?不知道大家知不知道它的原理是什么?今天我们就来说一说吸尘器究竟是如何替“吃灰”的。
    的头像 发表于 03-07 21:17 849次阅读
    吸尘器究竟是如何替<b class='flag-5'>你</b>“吃灰”的【其利天下技术】

    昨天看到消息Altera从Intel独立出来了,不知道大家常用的FPGA是什么?

    昨天看到消息Altera从Intel独立出来了,不知道大家常用的FPGA是什么?我这边分成常规生产治具是altera的,算法和图像相关的使用的是Xilinx的;
    发表于 03-06 13:39

    如何知道嵌入式电子控制单元 (ECU) 中的RAM使用情况?

    知道嵌入式软件构建工具会报告程序闪存使用情况。我认为他们也报告 RAM 使用率,但他们是否报告最大 RAM 使用率? 生成工具可能不知道在运行时将使用多少堆。是否有构建工具不知道的其他 RAM 使用情况? 如何准确找出运行时使
    发表于 01-22 07:02

    可能不知道的ADI Trinamic(L-Maxim)产品

    熟悉Trinamic产品的客户,经常会有这样的问题:–Trinamic的步进和伺服芯片性能很好,有没有其他类型电机的驱动芯片?–我们有自己的步进驱动算法,Trinamic集成的算法我们用不上,是否有可以配合的产品?–是否有高电压大电流的电机驱动芯片?–我们大量使用电磁阀和有刷电机,驱动方案很多但控制效果不够好,Trinamic有这类产品吗?原Maxim(美信
    的头像 发表于 01-12 08:10 480次阅读
    您<b class='flag-5'>可能不知道</b>的ADI Trinamic(L-Maxim)产品

    直插大功率电感不知道怎么选就看这里

    直插大功率电感不知道怎么选就看这里 编辑:谷景电子 关于直插大功率电感的选型一直是一个难题,要想直插大功率电感的优势在电路中发挥着作用,那么选型这个环节是必不可少并且特别重要。如果对直插大功率电感
    的头像 发表于 01-04 22:46 394次阅读

    7种MOSFET栅极电路的常见作用,不看不知道

    7种MOSFET栅极电路的常见作用,不看不知道
    的头像 发表于 12-15 09:46 1895次阅读
    7种MOSFET栅极电路的常见作用,不看<b class='flag-5'>不知道</b>!

    揭秘pcb是什么物质:不知道的“化学战士”

    揭秘pcb是什么物质:不知道的“化学战士”
    的头像 发表于 12-14 10:27 993次阅读