文章目录
一、输出
名称 | 用法 |
---|---|
putchar() | 只能输出单个字符 |
puts() | 只能输出字符串,并且输出结束后会自动换行 |
printf() | 可以输出各种类型的数据 |
对于printf()函数的格式控制符完整形式如下:
%[flag][width][.precision]type
1) type
tpye表示输出类型,比如 %d、%f、%c、%lf,type 就分别对应 d、f、c、lf;再如,%-9d中 type 对应 d。
type 这一项必须有,这意味着输出时必须要知道是什么类型。
汇总一下常见的格式控制符:
格式控制符 | 说明 |
---|---|
%c | 输出一个单一的字符 |
%hd、%d、%ld | 以十进制、有符号的形式输出 short、int、long 类型的整数 |
%hu、%u、%lu | 以十进制、无符号的形式输出 short、int、long 类型的整数 |
%ho、%o、%lo | 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
%#ho、%#o、%#lo | 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
%hx、%x、%lx;%hX、%X、%lX | 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
%#hx、%#x、%#lx;%#hX、%#X、%#lX | 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
%f、%lf | 以十进制的形式输出 float、double 类型的小数 |
%e、%le;%E、%lE | 以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
%g、%lg;%G、%lG | 以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
%s | 输出一个字符串 |
%p、%P | 以十六进制的形式输出数据的地址。P的大小写决定了十六进制前缀的大小写形式。(这里的地址都是虚拟地址,不等于数据在内存中的物理地址) |
注意:
- 一个数字,是由默认类型的:对于整数,默认是int类型;对于小数,默认是double类型;
- %g默认保留六位有效数字,包括整数和小数部分;%f和%e默认保留六位小数,只包括小数部分;
- %g不会在最后强加0来凑够有效数字的位数,但是%f和%e会在最后强加0来凑够小数部分的位数;
- 从上表中能看出,能够以有符号形式输出的只有十进制的整数
2) width
width表示最小输出宽度,也就是至少占用几个字符的位置;
当输出结果的宽度不足 width 时,以空格补齐(如果没有指定对齐方式,默认会在左边补齐空格);当输出结果的宽度超过 width 时,width 不再起作用,按照数据本身的宽度来输出。
3) .precision
.precision 表示输出精度,也就是小数的位数
- 当小数部分的位数大于 precision 时,会按照四舍五入的原则丢掉多余的数字;
- 当小数部分的位数小于 precision 时,会在后面补 0。
另外,.precision 也可以用于整数和字符串,但是功能却是相反的:
- 用于整数时,.precision 表示最小输出宽度。与 width 不同的是,整数的宽度不足时会在左边补 0,而不是补空格。
- 用于字符串时,.precision 表示最大输出宽度,或者说截取字符串。当字符串的长度大于 precision 时,会截掉多余的字符;当字符串的长度小于 precision 时,.precision 就不再起作用。
4) flag
flag 是标志字符。例如,%#x中 flag 对应 #,%-9d中 flags 对应-。下表列出了 printf() 可以用的 flag:
标志字符 | 含义 |
---|---|
- | - 表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐 |
+ | 用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号 |
空格 | 用于整数或者小数,输出值为正时冠以空格,为负时冠以负号 |
# | 对于八进制(%o)和十六进制(%x / %X)整数,# 表示在输出时添加前缀:八进制的前缀是 0,十六进制的前缀是 0x / 0X。对于小数(%f / %e / %g),# 表示强迫输出小数点。如果没有小数部分,默认是不输出小数点的,加上 # 以后,即使没有小数部分也会带上小数点 |
二、输入
1、输入函数
名称 | 用法 |
---|---|
getchar()、getche()、getch() | 这三个函数都用于输入单个字符 |
gets() | 获取一行数据,并作为字符串处理 |
scanf() | 可以输入多种类型的数据 |
2、getchar()、getche()、getch()区别
函数 | 作用 | 缓冲区 | 头文件 | 回显 | 适用平台 |
---|---|---|---|---|---|
getchar() | 获取一个字符(需要回车) | 有 | stdio.h | 有 | Windows、Linux、Mac OS 等所有平台 |
getche() | 获取一个字符(无需回车) | 无 | conio.h | 有 | Windows |
getch() | 获取一个字符(无需回车) | 无 | conio.h | 无 | Windows |
3、gets与scanf区别
get函数:获取字符串。gets() 是有缓冲区的,每次按下回车键,就代表当前输入结束了。
gets函数在新版C++中被移除了,因为不安全。可以用fgets代替,但注意fgets不会删除行末的回车字符。
gets() 和 scanf() 的主要区别是: 非常非常重要
- scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
- gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。
故对于输入一串字符串,若字符串中包含有空格,则需要使用gets()函数才能正确输入。
4、scanf函数
scanf函数,格式控制符与printf函数相差无几。只是scanf没有了%p地址输出控制符号。
&称为取地址符,也就是获取变量在内存中的地址。
scanf() 控制字符串的完整写法为:
%{*} {width} type
其中,{ } 表示可有可无。各个部分的具体含义是:
- type表示读取什么类型的数据,例如 %d、%s、%[a-z]、%[^\n] 等;type 必须有。
- width表示最大读取宽度,可有可无。
- *表示丢弃读取到的数据,可有可无。
5、scanf函数的符号匹配(超重点)
匹配特定字符 %[xxx]
使用连字符 -
常用的连字符举例:
- %[a-z]表示读取 abc…xyz 范围内的字符,也即小写字母;
- %[A-Z]表示读取 ABC…XYZ 范围内的字符,也即大写字母;
- %[0-9]表示读取 012…789 范围内的字符,也即十进制数字。
你也可以将它们合并起来,例如:
- %[a-zA-Z]表示读取大写字母和小写字母,也即所有英文字母;
- %[a-z-A-Z0-9]表示读取所有的英文字母和十进制数字;
- %[0-9a-f]表示读取十六进制数字。
不匹配某些字符
scanf() 允许我们在%[ ]中直接指定某些不能匹配的字符,具体方法就是在不匹配的字符前面加上^。例如:
- %[^\n]表示匹配除换行符以外的所有字符,遇到换行符就停止读取;
- %[^0-9]表示匹配除十进制数字以外的所有字符,遇到十进制数字就停止读取。
使用 scanf() 清空缓冲区的方案,就是:
scanf("%*[^\n]"); scanf("%*c");
下面我们就来解释一下。
首先需要明白的是,等到需要清空缓冲区的时候,缓冲区中的最后一个字符一定是换行符\n,因为输入缓冲区是行缓冲模式,用户按下回车键会产生换行符,结束本次输入,然后输入函数开始读取。
scanf("%*[^\n]"); 将换行符前面的所有字符清空,scanf("%*c"); 将最后剩下的换行符清空。
Scanf函数从缓冲区读取数据时的一些特性
当遇到 scanf() 函数时,程序会先检查输入缓冲区中是否有数据:
- 如果没有,就等待用户输入。用户从键盘输入的每个字符都会暂时保存到缓冲区,直到按下回车键,产生换行符\n,输入结束,scanf() 再从缓冲区中读取数据,赋值给变量。
- 如果有数据,那就看是否符合控制字符串的规则:
- 如果能够匹配整个控制字符串,那最好了,直接从缓冲区中读取就可以了,就不用等待用户输入了。
- 如果缓冲区中剩余的所有数据只能匹配前半部分控制字符串,那就等待用户输入剩下的数据。
- 如果不符合,scanf() 还会尝试忽略一些空白符,例如空格、制表符、换行符等:
- 如果这种尝试成功(可以忽略一些空白符),那么再重复以上的匹配过程。
- 如果这种尝试失败(不能忽略空白符),那么只有一种结果,就是读取失败。
注意:
- 换行符也是一个字符,也会进入缓冲区。
- scanf()、gets() 在读取字符串时会忽略换行符,不会把换行符作为字符串的内容。
- 空白符在大部分情况下都可以忽略。但是当控制字符串不是以格式控制符 %d、%c、%f 等开头时,空白符就不能忽略了,它会参与匹配过程,如果匹配失败,就意味着 scanf() 读取失败了。(这个结论是实践得出的结论,没有资料可以查询)
三、关于缓冲区的一些总结
1、一些概念
缓冲区(Buffer)又称为缓存(Cache),是内存空间的一部分。缓冲区是为了让低速的输入输出设备和高速的用户程序能够协调工作,并降低输入输出设备的读写次数。
根据不同的标准,缓冲区可以有不同的分类。
-
根据缓冲区对应的是输入设备还是输出设备,可以分为输入缓冲区和输出缓冲区。
-
根据数据刷新(也可以称为清空缓冲区,就是将缓冲区中的数据“倒出”)的时机,可以分为全缓冲、行缓冲、不带缓冲。
1)全缓冲
在这种情况下,当缓冲区被填满以后才进行真正的输入输出操作。缓冲区的大小都有限制的,比如 1KB、4MB 等,数据量达到最大值时就清空缓冲区。
在实际开发中,将数据写入文件后,打开文件并不能立即看到内容,只有清空缓冲区,或者关闭文件,或者关闭程序后,才能在文件中看到内容。这种现象,就是缓冲区在作怪。
2)行缓冲
在这种情况下,当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作。行缓冲的典型代表就是标准输入设备(也即键盘)和标准输出设备(也即显示器)。
Linux和macOS系统中printf和scanf函数都具有行缓冲区。
3)不带缓冲
不带缓冲区,数据就没有地方缓存,必须立即进行输入输出。
getche()、getch() 就不带缓冲区,输入一个字符后立即就执行了,根本不用按下回车键。
Windows 下的 printf() 也不带缓冲区,不管最后有没有换行符\n,都会立即输出,所以对于类似的输出代码,Windows 和 Linux、Mac OS 会有不同的表现。
错误信息输出函数 perror() 也没有缓冲区。错误信息必须刻不容缓、立即、马上显示出来,缓冲区将会增加捕获错误的时间,这是毫无理由的。
总结
- 输入:scanf()、getchar()、gets()都具有行缓冲,Windows 下特有的 getche() 和 getch(),不带缓冲区;
- 输出:Windows 平台下,输出设备是不带缓冲区的,Linux 和 Mac OS 平台下,输出设备带有行缓冲区。
2、缓冲区的刷新(清空)
所谓刷新缓冲区,就是将缓冲区中的内容送达到目的地。缓冲区的刷新遵循以下的规则:
- 不管是行缓冲还是全缓冲,缓冲区满时会自动刷新;
- 行缓冲遇到换行符\n时会刷新;
- 关闭文件时会刷新缓冲区;
- 程序关闭时一般也会刷新缓冲区,这个是由标准库来保障的;
- 使用特定的函数也可以手动刷新缓冲区。