目录
一、格式控制符
整数
小数
地址
二、输出函数
printf()
puts()
putchar()
三、输入函数
scanf()
getchar()、getche()、getch()
四、缓存区
缓冲类型
缓冲区大小
结合缓存区谈输入函数
缓冲区的刷新(清空)
读取一个整数时可带前缀也可不带,在格式控制符中加上 # 可输出前缀。
|
short |
int |
long |
unsigned short |
unsigned |
unsigned long |
八进制 |
|
|
|
%ho |
%o |
%lo |
十进制 |
%hd |
%d |
%ld |
%hu |
%u |
%lu |
十六进制 |
|
|
|
%hx(%hX) |
%x(%X) |
%lx(%lX) |
%g、%lg 既可以读取一个十进制形式的小数,也可以读取一个指数形式的小数
|
float |
double |
十进制形式 |
%f |
%lf |
指数形式 |
%e(%E) |
%le(lE) |
较短形式 |
%g(%G) |
%lg(%lG) |
|
float |
带前缀 |
%#x(%#X) |
不带前缀 |
%p(%P) |
printf() 可以输出各种类型的数据,是最灵活、最复杂、最常用的输出函数,可以替代其他输出函数。
一般格式:
printf("格式控制字符串", 参数列表);
格式控制字符串:用于控制输入数据格式,必须以引号引导,内容由一个或多个格式控制字符组合而成,也可以含普通字符,普通字符按原样在对应位置输出,也可以含转义字符。
参量列表:需要输出的一系列参数, 其个数和顺序必须与格式化字符串所说明的输出参数个数一样多, 各参数之间用逗号隔开。
输出格式控制符:
%[flag][width][.precision]type
// flag:标志字符
- : 表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐
+ : 用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号
空格 : 用于整数或者小数,输出值为正时冠以空格,为负时冠以负号
# : 用于八进制和十六进制整数时,表示在输出时添加前缀; 用于小数时,表示强迫输出小数点
// width:最小输出宽度(至少占用几个字符的位置)
当输出结果的宽度不足 width 时,以空格补齐(如果没有指定对齐方式,默认会在左边补齐空格)
当输出结果的宽度超过 width 时,width 不再起作用,按照数据本身的宽度来输出。
// .precision:输出精度
用于小数时表示小数的位数:
当小数部分的位数大于 precision 时,会按照四舍五入的原则丢掉多余的数字
当小数部分的位数小于 precision 时,会在后面补 0。
用于整数时表示最小输出宽度:
与 width 不同的是,整数的宽度不足时会在左边补 0,而不是补空格。
用于字符串时表示最大输出宽度:
当字符串的长度大于 precision 时,会截掉多余的字符
当字符串的长度小于 precision 时,.precision 就不再起作用。
只能输出字符串,并且输出结束后会自动换行
每次只能输出单个字符,输出多个字符需要调用多次
通用的输入函数,它可以读取多种类型的数据,是最灵活、最复杂、最常用的输入函数,但它不能完全取代其他函数
格式:
scanf("格式控制字符串", 变量地址列表);
格式控制字符串:用于控制输入数据格式,必须以引号引导,内容由一个或多个格式控制字符组合而成,也可以含有非格式控制字符,非格式控制字符称为普通字符,普通字符按原样在对应位置输入。格式控制字符串中一般不使用转义字符,否则要按原样在对应位置从键盘输入,为输入数据带来不必要的麻烦。
变量地址列表:用于指定存放数据的变量地址,scanf 会根据地址把读取到的数据写入内存。如果需要给多个变量输入数据,则各变量地址要用逗号隔开。
输入格式控制符:
%[*][width]type
// * : 输入赋值抑制字符,表示该格式说明要求输入数据,但不赋值,也即在地址列表中没有相应的地址项
// width : 宽度指示符,表示该输入项最多可输入的字符个数。如遇空格或不可转换的字符,读入的字符将减少
使用scanf函数对实型变量进行赋值时,在格式%f中不得控制小数位的精度。
scanf( ) 要求输入数据的格式要和控制字符串的格式保持一致。但对输入数据之间的空格的处理比较宽松,并不要求空格数严格对应,只要有空格就行。
scanf() 也可以读取带空格的字符串:
scanf("%[^\n]", str); // 直到输入'\n'才结束读取
%[]的意义是读入一个字符集合。[]是个集合的标志,因此%[]特指读入此集合所限定的那些字符,比如%[A-Z]是输入大写字母,一旦遇到不在此集合的字符便停止。如果集合的第一个字符是 "^",这说明读取不在 "^" 后面集合的字符,即遇到 "^" 后面集合的字符便停止。
这三个函数都用于输入单个字符。getche( )、getch( )在缓冲区和回显方面与 scanf() 有着不同的特性,是 scanf() 不能替代的,但不是标准函数,只能用于 Windows,使用时要加上头文件 conio.h
getchar():
getchar()就是scanf("%c", c)的替代品,除了更加简洁,没有其它优势了;或者说,getchar() 就是 scanf() 的一个简化版本。
getche():
getche()没有缓冲区,输入一个字符后会立即读取,不用等待用户按下回车键,这是它和 scanf()、getchar() 的最大区别。
getch():
getch() 和 getche() 相同,也没有缓冲区。
getch() 的特别之处是它没有回显,看不到输入的字符。所谓回显,就是在控制台上显示出用户输入的字符;没有回显,就不会显示用户输入的字符,就好像根本没有输入一样。
回显在大部分情况下是有必要的,它能够与用户及时交互,让用户清楚地看到自己输入的内容。但在某些特殊情况下,我们却不希望有回显,例如输入密码,有回显是非常危险的,容易被偷窥。
缓冲区(Buffer)又称为缓存(Cache),是内存空间的一部分。也就是说,在内存中预留了一定的存储空间,用来暂时保存输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
应用缓冲区可大大提高计算机的运行速度。比如从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作。
缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
全缓冲:当填满缓冲区后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
行缓冲:当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。
不带缓冲:也就是不进行缓冲,标准错误文件 stderr 是典型代表,这使得出错信息可以直接尽快地显示出来。
ANSI C对stdin、stdout和stderr的缓存特征没有强行的规定,以至于不同的系统可能有不同的stdin、stdout和stderr的缓存特征。目前主要的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。
如果没有自行设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是512个字节的大小。
缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义,如果希望查看它的大小,包含头文件,直接输出它的值即可: printf("%d", BUFSIZ);
缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,通过使用 setvbuf() 和 setbuf() 函数
从本质上讲,我们从键盘输入的数据并没有直接交给 scanf(),而是暂时保存到缓冲区中,直到我们按下回车键,scanf() 才从缓冲区中读取数据,赋值给变量。如果缓冲区中的数据符合 scanf() 的要求,那么就读取结束;如果不符合要求,那么就继续等待用户输入,或者干脆读取失败。scanf() 匹配到想要的数据后,会将匹配到的数据从缓冲区中删除,而没有匹配到的数据仍然会留在缓冲区中。
正是由于缓冲区的存在,才使得我们能够多输入一些数据,或者一次性输入所有数据。
scanf( ) 、getchar( ) 是带有缓冲区的。遇到 scanf( ) 和getchar( )函数,程序会先检查输入缓冲区中是否有数据。如果有数据,scanf( ) 会直接读取,不会等待用户输入;如果没有,就等待用户输入。而 getche()、getch() 不带缓冲区,只能立即读取。
其实当用户按下回车键时,回车换行符也会被保存到缓冲区,只是大多数情况下 scanf() 会忽略。但是当控制字符串不是以 %xxx 开头时,回车换行符就起作用了,scanf() 会对它进行匹配,只是匹配失败而已。所以有时候要清空缓存区。
下列情况会引发缓冲区的刷新:
scanf() 的缓冲区有时会引发奇怪的问题,多个 scanf() 之间要注意清空缓冲区。
清空缓冲区主要有两种思路:一是将缓冲区中的数据丢弃,二是将缓冲区中的数据读取出来,但是却不使用。
如果只考虑Windows,建议使用fflush(stdin);,简单明了;如果兼顾移植和效率,建议使用scanf("%*[^\n]%*c"); 。
1. 使用fflush( ) 清空文件缓冲区:
fflush(stdin);
fflush(stdin) 直接将缓冲区中的数据丢弃,它在 Windows 下一般是有效的,但在 Linux GCC 下可能无效,因为C语言标准规定:对于以 stdin 为参数的 fflush() 函数,它的行为是不确定的,fflush() 操作输入流是对标准C语言的扩充。
2. 使用 scanf( ) 循环读取缓冲区中的数据:
scanf("%*[^\n]%*c");
%*[^\n]将逐个读取缓冲区中的 '\n' 字符之前的其它字符,% 后面的 * 表示将读取的这些字符丢弃,遇到 '\n' 字符时便停止读取。此时,缓冲区中尚有一个 '\n' 字符遗留,所以后面的%*c将读取并丢弃这个遗留的换行符,这里的星号和前面的星号作用相同。由于所有从键盘的输入都是以回车结束的,而回车会产生一个 '\n' 字符,所以将 '\n' 连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区
3. 使用 getchar( ) 循环读取缓冲区中的数据:
int c;
while((c = getchar()) != '\n' && c != EOF);
该代码不停地使用 getchar() 获取缓冲区中的字符,直到获取的字符是换行符\n或者是文件结尾符EOF为止。这个方法可以完美清空输入缓冲区,并且具备可移植性。但需要额外定义一个 char 类型的变量,略显繁琐,并且 while 循环也会导致效率不高,所以建议使用scanf("%*[^\n]%*c"); 。