printf函数:
继续IOS的C基础之旅,对于printf函数来说应该都非常之熟了,除了C有这个函数外,像Koltin也是有它,这里还是对它进行一个梳理,它里面的有些细节如果未来忘了回来也可以查询一下,很简单但是有必要把它的一些值得记录的细节给记录下来,看下面。
域宽问题:
![](https://i-blog.csdnimg.cn/blog_migrate/197538ee9560a82bb6e1e3d32c9f0e87.png)
下面来实验一下:
![](https://i-blog.csdnimg.cn/blog_migrate/77b6bad6a3014bac7148e5b9212ef567.png)
上面代码没啥好说的,数字本身只有2个默认输出也就是实际数字的长度,但是如有需求需要输出固定为5的数字长度呢?则可以这样:
![](https://i-blog.csdnimg.cn/blog_migrate/989207cdc49d7cc835abb95cb721efc8.png)
也就是默认情况下位宽是右对齐, 那如果要想变为左对齐,改为负数既可,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/931204737ddcc98034a9bd07cd8598b0.png)
再来看一个需要注意的例子:
![](https://i-blog.csdnimg.cn/blog_migrate/f0743ff64e32be0141e3c39c41b46cb6.png)
也就是说如果指定了位宽,但是实际输出的内容超出了宽度,会按照实际的的宽度来进行输出的。
对于位宽的使用一般会搭配通配符来使用,比如数字1,可能想输出01,也就是位宽不够按0来补,最典型的就是日期的输出,比如:2020-09-26,使用如下:
![](https://i-blog.csdnimg.cn/blog_migrate/737f63127e023bf742eb1b7207b1f7c6.png)
浮点型问题:
在正式写代码之前,这里先学一个Xcode的小技巧,就是将咱们的注释代码给折叠起来,先来将代码进行注释:
![](https://i-blog.csdnimg.cn/blog_migrate/f6e9f741104700679d0fbeba1174f469.png)
然后将光标定位到注释代码中,然后按“option+command+方向左”则会收起注释代码,而展开代码则是按“option+command+方向右”,下面来试一下:
![](https://i-blog.csdnimg.cn/blog_migrate/94c61858bcd8ddeed3522042cb443edf.gif)
这样可以在阅读代码时根据需要来隐藏与显示。
接下来则来定义一个浮点小数:
![](https://i-blog.csdnimg.cn/blog_migrate/9ad0864b27c1f839f8eafd4b0fbec14e.png)
2、
![](https://i-blog.csdnimg.cn/blog_migrate/8561bab8aad305460a08ef6446d60c2a.png)
3、%f的各种坑:
![](https://i-blog.csdnimg.cn/blog_migrate/08998312e0608c4e87519fe0198181b4.png)
那,我就是想要输出这么多小数位呢?不简单,该小数的位数为10位,那这样改一下呗:
![](https://i-blog.csdnimg.cn/blog_migrate/8011471df3adb1d7a80177ccb404a2c5.png)
这是因为:
![](https://i-blog.csdnimg.cn/blog_migrate/820e505571c71f90b050d35fc0848764.png)
其中对于“有效位”需要注意:
![](https://i-blog.csdnimg.cn/blog_migrate/57c8ccc482c147a7ef6ed091b86bc72d.png)
回到咱们程序而言:
![](https://i-blog.csdnimg.cn/blog_migrate/e7a6e011a0e243e8eae26f17a7d244ef.png)
那要想完整的输出上面的数字,则需要将它转成double才行,因为它的有效位是15,很显然能满足咱们的输出要求,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/86cb181abe4883bfdf0bb27fa69cd99b.png)
4、有逼格的写法【了解】:
对于指定保留小数位还有另外一种写法,就是指定保留多少小数位时,可以通过*号占位,啥意思,看个例子就明白了:
![](https://i-blog.csdnimg.cn/blog_migrate/9b31dd73243b8224a88952c5fb09798e.png)
这种用法实际用得不多,有个了解既可。
![](https://i-blog.csdnimg.cn/blog_migrate/242909c57e1cd7be62d9add4d15a02b1.png)
scanf函数:
格式:
![](https://i-blog.csdnimg.cn/blog_migrate/14095b8045e4a5393af1752209277861.png)
其中格式控制字符符可以有如下这些:
![](https://i-blog.csdnimg.cn/blog_migrate/b0941f888fc64b758a93f9d5ee7cbdd0.png)
这里主要是关注输入多个数的情况,有坑,先来看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/8e58cce48873b58aafcf2d601e3c33b6.png)
这样的代码是非常之熟悉了,下面先运行看一下,结果会出乎你的意料之外:
![](https://i-blog.csdnimg.cn/blog_migrate/dc5f0cbfe225209196913fedd3b946e1.gif)
呃,为啥我回车想输入第二个值时直接就输出了呢?好,继续再来看另一种情况:
![](https://i-blog.csdnimg.cn/blog_migrate/0d5185dca0126a88a5f0f2d31d4d7dac.gif)
两个数之间用了一个“-”来区分,结果第二个变量还是木有正常被赋值, 这是为啥呢?这是因为“利用scanf接收多个数据的时候,输入的内容需要和“格式化字符串”中的一模一样,如果输入的数据跟格式化字符串中的不匹配,那么scanf函数会自动终止。”,回到咱们的程序来解释一下,scanf的格式化字符串中间是以逗号分隔的:
![](https://i-blog.csdnimg.cn/blog_migrate/a6b42883f192223df2123495e085291d.png)
所以对于上面两次的运行都完全不匹配这种,很显然num2都没得到赋值scanf就被终止了,正常的结果得这样:
![](https://i-blog.csdnimg.cn/blog_migrate/dc9ff0289dbbaa7b8a03f68d288d9ca4.gif)
好,接下来继续来填坑,修改一下程序:
![](https://i-blog.csdnimg.cn/blog_migrate/e5c7f498fea78e4f3cd032b2346e7b60.png)
当然这块也比较简单,直接在输入的时候加上“空格、回车、TAB”来做为区分既可,这块也是比较熟的语法,试一下:
![](https://i-blog.csdnimg.cn/blog_migrate/af8c9df9424d7f6f2a0a63e00987656b.gif)
一切是如此的顺畅,但是!!!这种写法如果scanf的“格式化字符串”中不允许有%c出现,否则各种意料之外的事则会发生,下面来看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/3e53fe6c6a205f566c1d7ab442e9c3b9.png)
这是因为敲空格也是一个字符,刚好跟咱们的%c类型是一样的,所以就错乱了,其实对于输入多个类型的简单一点就是给每个数据类型中增加一个分隔符来解决像上面的各种坑,比如:
![](https://i-blog.csdnimg.cn/blog_migrate/e58f96b4c693c7a4913624c2e8e44f5b.png)
继续再来看另一种坑,重新编写个代码:
![](https://i-blog.csdnimg.cn/blog_migrate/2d6c8fa4ca24f1355d57280615a5320b.png)
那如果这样改呢?
![](https://i-blog.csdnimg.cn/blog_migrate/0f24e7ab947bbece6a4f15079e4ede1e.png)
下面再来运行:
![](https://i-blog.csdnimg.cn/blog_migrate/9496aef4c9620797974ea0d9bc769c9a.gif)
这是因为‘\n’是scanf的结束符,所以不能在scanf的格式化字符串末尾写上‘\n’,那既然不能将'\n'放末尾,那放到中间呢?试一下:
![](https://i-blog.csdnimg.cn/blog_migrate/d1eb78ebc2198689612d2d73b2d57dcf.png)
运行:
![](https://i-blog.csdnimg.cn/blog_migrate/dcf100302fadea02e7350b0d2aed0227.gif)
也就是如果放在非末尾输入时按要求输是能正常接收的,那对于它放在末尾的情况就无法破么,其实也不是,下面再来在末层加上一个'\n',准备破它:
![](https://i-blog.csdnimg.cn/blog_migrate/eac3c91df9da5327393aec8ca1c379e8.png)
运行:
![](https://i-blog.csdnimg.cn/blog_migrate/f5aa118ed61f31963f8a2933344afec3.gif)
也就是当遇到不匹配的字符其scanf就会结束了,所以随便输入一个不是'\n'的字符既可。这些细节可能实际中不一定会遇到,但是需要知道可能存在的坑。
scanf函数实现原理:
接下来探究一下scanf它的实现原理,先来再复习一下它的基本使用:
![](https://i-blog.csdnimg.cn/blog_migrate/5757140b0d4c6fa37017a1c505aff55d.png)
接下来基于它来修改一下,开始要阐述原理了,比较经典,之前其实已经看到过这程序:
![](https://i-blog.csdnimg.cn/blog_migrate/41c8539e396116009cd9b49d3a6f0395.png)
为啥会这样呢?这里从scanf的原理来解释一下:
![](https://i-blog.csdnimg.cn/blog_migrate/8b25adccff74f52347d03da54635b2ff.png)
接着遇到了scanf函数则会从输入缓冲区中获取用户数据,根据匹配的类型一一从缓冲区中来取,第一个需要的是一个整型,很显然输入缓冲区中刚好满足,此时就从中读取赋值给了num1了:
![](https://i-blog.csdnimg.cn/blog_migrate/b7f479f0a28465bc0bc4c9f292cb3ad3.png)
同样的,第二个是需要一个字符,而空格不刚刚好就是字符么?所以类似的将空格取出来赋值给了charValue了:
![](https://i-blog.csdnimg.cn/blog_migrate/90181e360a4e81b032ecb619774a5350.png)
一切都是那么的顺畅,接下来则需要从输入缓冲区中取出+赋值给num2了,很显然类型不匹配:
![](https://i-blog.csdnimg.cn/blog_migrate/4fabf597a30810a7e7ca2d4580813d35.png)
如前面所阐述的,只要是类型不匹配,那么scanf函数就会自动结束并且不会修改对应的变量的值,也不会取走输入缓冲区中保存的内容, 所以此时为啥num2是输出的0就知道啦,这里接下来重点要来关注剩下输入缓冲区中的数据了:
![](https://i-blog.csdnimg.cn/blog_migrate/3ce77866e99b1957ba9a11d61347e3b6.png)
好!!接下来再来修改一下代码就比较神奇了,也能道出scanf的原理之所在:
![](https://i-blog.csdnimg.cn/blog_migrate/6bb8b02376b7f826328d5113bb4d965c.png)
运行看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/0effd9c0ea443de79cca6dc3efe0be86.gif)
不是scanf是一个阻塞式的函数么?为啥第二次的scanf木有等用户输入就直接输出了呢?这点就需要注意了,当scanf输入缓冲区中如果还有内容,就不会要求用户输入数据了,所以看一下输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/9766a89617dee9278bb7ae0567824fc8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/461b4adaa19ecf98283ed63dababe2cc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/be35f9c963437543d9cf7a867d162f12.png)
以上就是scanf函数的一个运作机制。