scanf()、printf()、getchar()和putchar()这四个函数能够让用户和程序交流,所以被称为输入/输出函数,或简称为I/O函数。
这里先结合缓冲输入来讲讲scanf()和getchar()背后的处理机制。还有空格回车输入跟scanf()、getchar()的关联。
对于缓冲输入而言,键盘的输入不是实时被scanf响应的。首先存在缓冲当中。
当遇到换行、缓冲满、程序结束几种情况之一的时候,才会被送到scanf中。
而且scanf是以删除的方式从缓冲区读入数据(来自标准输入设备的数据存储在缓冲区)。读一个数据,该数据就在缓冲区被删掉。
如果scanf需要读取一个数据项,发现缓冲区当前是空的,那么程序就会在scanf代码处阻塞,等待用户输入。
只有scanf函数接收到相应数据项之后,在缓冲区这个数据项清楚,scanf函数返回,程序继续执行。
空白字符(white space)的概念:包含了空格(space)、回车(enter)、制表符(table);
scanf()返回的是成功赋值的数据项数。如果出错或遇到end of file(文件结尾),则返回EOF;
(如果想从键盘输入EOF,使用Ctrl+D或Ctrl+Z)
getchar()是到输入流数据缓冲区一个一个读取数据,调用一次读取缓冲区的首字符,并且删掉该字符。
行缓冲输入:以换行为结束标志;scanf()调用会等待用户按键,当用户按键完之后,就会去缓冲区读数据。
getchar()则是直接去读缓冲区的内容。
例子:混合使用getchar()和scanf()
1 //检查输入是否正确。要求输入是整数
2 int get_int(void)
3 {
4 int input;
5 char ch;
6
7 while(scanf("%d",&input)!=1)
8 {
9 while((ch=getchar())!='
')
10 putchar(ch);
11 printf(" is not an integer.
Please enter an");
12 printf("integer value,such as 25,-178,or 3:
");
13 }
14 return input;
15 }
以上是一个用户输入进行验证的函数,不符合要求的输入,会提示用户再次输入。
当调用该函数的时候,直接敲Enter就会导致scanf()阻塞,光标闪烁,继续等待用户输入。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
I/O函数的具体用法和说明:
1、printf()函数:
打印数据的指令要与待打印数据的类型相匹配。这些符号被称为转换说明。conversion specification
格式:printf(格式字符串,待打印项1,待打印项2…)
格式字符串中要包含待打印项的转换说明。格式字符串是双括号括起来的内容。%符号用来表示转换说明。打印%符号就成了一个问题,解决方法很简单,用两个%%即可。
转换说明把二进制格式存在计算机中的值转换成一系列字符以便于显示。
转换说明与待打印值不匹配会导致一些问题,匹配非常重要。
大部分C函数都有一个返回值,这是函数计算并返回给主调程序(calling program)。
printf()也有返回值,但不是主要功能,是附带用途。检查输出错误时可以用到。
返回打印字符的个数(项数),如果打印错误,会返回一个负值;返回成功打印的项数(成功打印要保证输入符合转换说明的要求);
另外还有转换说明修饰符的概念;
2、scanf()函数
scanf()返回值:
scanf()只返回成功读取的项数;如果没有读取任何项,且只需要读取一个数字而用户却输入一个非数值字符串,scanf()便返回0;
scanf()遇到“文件结尾”时,会返回EOF(EOF是stdio.h中定义的一个特殊值,#define指令把EOF定义为-1);
结合if语句和while语句,可使用scanf()的返回值来检测和处理不匹配输入的问题。
scanf什么时候加&,什么时候不加&:scanf的意思是,你把输入的值传到我的这个地址上。
如果你的参数已经是地址了,就不需要用&;
当参数为数据(变量名)时,则使用取址符&;
当参数为地址时,不需要使用取址符&,例如字符串、指针;
3、getchar()和putchar()函数:
二者都属于单字符I/O,只处理字符,而且是一个一个字符地处理。
具体用法:待补充
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
验证输入:
是代码中最复杂的部分:有多种处理方案,可以在用户输入错误的信息时,终止程序。也可以给用户提供有限次或无限次机会重新输入。
程序要做到:当用户遵循指令时程序运行正常。当用户不遵循指令时,程序也能运行正常。
输入实际上是字符流,getchar()函数逐字符读取输入。遵循模块化的编程思想,使用独立函数(模块)来验证输入和管理显示。
文件结尾:
无论操作系统使用何种方法检测文件末尾。C语言中,用getchar()读取到的文件结尾时,将返回一个特殊值,即EOF(end of file)的意思。scanf检测到文件结尾时也返回EOF。EOF定义在stdio.h文件中;#define EOF (-1)
为什么是-1,因为-1不对应任何字符,所以该值可用于表示文件结尾。
关键要理解EOF是一个值。标志着文件的结尾,并不是在文件中找得到的符号。
如何使用EOF,把getchar()的值与EOF作比较。如果两值不同,就说明没有到达文件结尾。(如果想从键盘输入EOF,使用Ctrl+D或Ctrl+Z,即模拟EOF)
上升一个层次:
C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示设备视为没个C程序自动打开的文件,stdin表示键盘输入,stdout表示屏幕输出。
设备即文件!
从概念上来讲,C处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流。
不同属性和不同种类的输入,由属性更统一的流来表示。于是打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
缓冲区的概念:
缓冲输入和无缓冲输入都有用武之地。
有些交互式程序也需要无缓冲输入,例如在游戏中,你希望按下一个键就执行相应的指令。
缓冲区(buffer):用户的输入被存储在一个临时存储区,按下Enter键之后,程序才可以使用用户输入的字符。
缓冲的分类:完全缓冲I/O和行缓冲I/O
完全缓冲I/O:当缓冲区被完全填满时才刷新缓冲区,通常出现在文件的输入中。缓冲区的大小取决于系统。
行缓冲I/O:出现换行符时刷新缓冲区。键盘输入通常是行缓冲,所以在按下Enter键后才刷新缓冲区。
刷新缓冲区(即内容被发送到目的地)
总结:
从流的角度去理解数据的输入输出、再结合缓冲区的概念。就能够深刻理解I/O函数背后的处理机制。
再结合重定向的概念,输入输出设备也是文件的概念。从更高层面上理解数据的流动。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
printf()和scanf()*修饰符:
printf()和scanf()都可以使用*修饰符来修改转换说明的含义。
如果预先不想指定字段宽度,希望通过程序来指定,那么可以用*修饰符代替字段宽度。
但是要用一个参数告诉函数,字段宽度应该是多少。
如果转换说明是%*d,那么参数列表应该包含*和d对应的值。
这个技巧也可以用于浮点值指定精度和字段宽度。
1 int width =3; 2 int precision=3; 3 float weight = 68.1233; 4 printf("Weight = %*.*f ",width,precision,weight);
返回值会变成:Weight = 68.123
scanf()中*的用法与此不同,把*放在%和转换字符之间。会使得scanf()跳过相应的输出项。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
参考链接:
C语言中scanf函数与空格回车:https://blog.csdn.net/u011499425/article/details/52606973