C语言深入标准输入输出( 二 )


所有数据修饰符如下表:
可以看到,()不能输出二进制,通过数字指定的宽度是最小宽度而不是绝对宽度,因此我们可以使用(“x”, );输出一个字节的十六进制整数,但是不能输出二进制整数,二进制必须使用itoa()转化为字符串输出 。()具有返回值,如果成功输出返回输出字符个数,如果输出错误会返回一个负值 。
scanf()
scanf()比()难掌握是因为它要与键盘进行交互,而且涉及到键盘缓冲区的硬件知识,还有按变量地址传入的方式涉及到指针,控制台均以字符串形式传入,因此又要将字符串转换为对应的数据类型 。我们首先看看scanf()的格式,再从最简单的问题入手 。scanf()函数的格式为:
scanf (“格式”, 地址1,地址2…)
首先scanf()要求传入变量地址而不是变量名,为什么呢?这是因为与()不同的是scanf()是赋值操作,也就是需要修改变量的值,而变量可能是数值也可能是字符串,按值传递还是按引用传递不确定,因此统一传入地址 。输入整型写为:scanf(“%d”, &a); 输入字符串为:scanf(%s, arr);
第二个问题是内容匹配,在scanf()中输入的格式要和定义的格式完全一致,例如scanf(“a=%d”,&a),必须输入a=1才行,否则会导致scanf()获取不到正确信息进入死循环或宣告失败 。当有多个变量需要赋值时,当匹配第一个格式符遇到空格、制表符这样的间隔符或者因为类型不同匹配失败后会继续给下一个匹配,如果缓冲区已没有数据则要求用户继续输入而不退出 。例如scanf(“%d%d%d”, &a,&b,&c)可以通过输入1 2 3或1 2 3等来连续赋值,当匹配第一个%d时遇到空格会导致匹配终止,接着匹配下一个%d,在匹配下一个整数时如果遇到空格会自动忽略,直到读取到数字 。这里不能输入1,2,3,因为逗号被看作非法字符,当给a赋值完后遇到逗号会停止匹配,而转入下一个%d的匹配,而匹配下一个%d时第一个读取的是逗号,这个非法字符不会像空格那样被忽略,又会导致匹配失败而进入下一个匹配,直到找不到匹配的内容宣告失败,结果只给a进行了赋值操作 。如果你只输入了1后回车,当a赋值完后发现缓冲区已没有数据,scanf()会继续让用户输入,当输入2后开始匹配第二个%d,此时键入的回车会被当做空白忽略而正确读到数字2,同样接着输入3可以完成所有的赋值操作,因此输入多个数值时可以使用空白也可以使用回车作为空白间隔符,如果你一定要使用逗号作为间隔符需要改为:scanf(“%d%,d,%d”, &a,&b,&c) 。
第三个问题是格式转换问题,标准输入输出操作的都是字符,控制台将所有类型转化为字符后进行输出,键盘键入的内容也是字符,因此在匹配类型的过程中本身就存在数据类型转换 。scanf()可以识别包含回车键在内的所有字符,空格、制表符和回车会被当做空白间隔符,()是将所有类型转换为字符输出,scanf()是将字符解析为数值,而将字符转化为数值相对困难,出错的机率更高 。例如要求输入整数但你输入一个小数,小数点会被当做非法字符导致匹配结束 。因此scanf()不仅要求输入内容的格式与给出的格式完全相同,还要求数据类型、甚至小数精度都严格匹配,比如short必须使用%hd,长整数必须使用%ld,双精度必须使用%lf,地址必须使用%p 。即便这样,也可能产生意外结果,那就是对于字符串输入,可能你认为输入一个带空格的字符串是一件正常的事情,但却和你想象的不同,scanf()遇到空格就认为字符串输入结束了,因此scanf()是不能接受带空格的字符串的 。
最后一个问题是缓冲区,这个问题最频繁也最复杂,因为它涉及到硬件的工作原理,如果不明白缓冲区运行机制,随时会被一些莫名其妙的问题缠住,要解答这个问题,我们只能从硬件知识入手 。缓冲区是为解决输入输出速度不对等的性能问题而设置的区域,由于不同的硬件运行速度不同,并且有些硬件访问需要等待的时间开销很大,响应缓慢,缓冲区既可以改善硬件之间的速度不对等问题,又可以减少硬件的访问次数,还可以实现异步工作 。速度不对等的交流比较典型的是内存和硬盘,硬盘读写速度远远小于内存,特别是机械硬盘,平时没有读写时处于睡眠状态,当需要访问时要经过请求访问、电机加速、磁头读写、再由主板南桥芯片负责将数据传输到内存等一系列复杂的操作,可以直接导致程序卡顿,例如将休眠的硬盘唤醒读取数据时非常明显 。如果在内存中开辟一个缓冲区,一次读取多个数据,由于后续的数据已经在缓冲区,就不必再从硬盘取出了,直接从缓冲区取出,解决了响应慢的问题,这就是所谓的预读 。如果需要写入数据,将内容先写入缓冲区,等待缓冲区填满再一次性写入硬盘,这样可以大大减少写入次数 。一些读写异常缓慢的硬件干脆采取异步传输,比如打印机,它依赖缓冲区储存临时数据,下载和打印时cpu可以做其它事情从而减少等待时间,这些异步硬件是不会造成计算机卡顿的 。现在的计算机硬件,如显卡、声卡、网卡等几乎都会在内存中开辟自己的缓冲区,硬件内部也有缓冲区,而且还将缓冲区分级,例如cpu、硬盘内部都按照速度不同分为一二三级缓存,它们在硬件架构内部自成体系 。在工作时硬件内部的缓冲区对接内存中的缓冲区,大大降低了硬件访问次数,缓解了输入输出不对等的问题 。我们再来看看软件,其实软件的运行也离不开缓冲区,抛开操作系统在底层给我们创造的缓冲区,我们自行编写软件时也会将频繁读写的数据放到内存中,或者将可能用到数据提前从硬盘取出,或者在程序空闲时将缓冲区的数据写入硬盘,这么一来缓冲区就分为硬件缓存、系统缓存和应用缓存,随处可见 。在C语言中缓冲区也频繁出现,很多表示内存地址的变量都使用buff这样的名称,它表示在内存中创建一段缓冲区 。