hello,csdn的小伙伴们,今天本外传主要来探讨一下输入输出函数以及他们的缓冲区这些方面的知识。希望大家都能收获到这些知识。
目录
当我们想要在输入一个字符,并且打印它的时候,我们头脑中最先想到的一定是scanf和printf,这两个函数搭配使用来输出并且打印。
- #include
- int main()
- {
- char n;
- scanf("%c", &n);
- printf("%c", n);
- return 0;
- }
这是最简单的一种方法了。
除此之外我们在来介绍两个函数,getchar和putchar
- #include<stdio.h>
- int main()
- {
- char n;
- //scanf("%c", &n);
- //printf("%c", n);
- n = getchar();//从键盘中获取一个字符
- putchar(n);//输出一个字符到屏幕上
- return 0;
- }
需要注意的是:scanf和printf可以针对各种各样的数据进行输入输出。但是getchar和putchar仅仅针对字符
我们来了解一下getchar这个函数,我们先进入一个网站,这个网站里面有着各种库函数的使用详解。c语言库函数,在这个库函数网站里面,我们在上面搜索getchar
搜索之后我们进入如下界面
在这里,我们会发现,int getchar (void),也就是说,getchar接收的参数是void,也就是空的意思,即不需要参数,然后返回一个int类型的整型数字,这个int的类型的整型数字就是从我们键盘上读取到的那个数字的ASCII值。它还是一个只获取一个字符类型的函数。但是如果获取失败,他就返回EOF,注意EOF就是-1。
对于scanf我们也同样了解一下。
成功时,函数返回成功填充的参数列表的项数。由于匹配失败、读取错误或到达文件末尾,此计数可以与预期的项数匹配,也可以更少(甚至为零)。
如果在读取过程中发生读取错误或到达文件末尾,则设置适当的指示符(feof或ferror)。并且,如果在成功读取任何数据之前发生任何一种情况,则返回EOF。
如果在解释宽字符时发生编码错误,该函数将errno设置为EILSEQ。
这是scanf的返回值,也就是说,scanf的返回值等于scanf成功读取到数据的个数。如果失败,则返回EOF,即-1。
举个例子,请看下面一段代码。
- #include
- int main()
- {
- int a, b,ret;
- ret = scanf("%d %d", &a, &b);
- printf("%d", ret);
- return 0;
- }
这段代码中如果输入两个正常的数,那么将会读取成功。打印结果为2
如果只成功输入一个数字,注意在输入函数中输入crtl+z就会读取失败一个数。但是visual stdio环境下scanf有一个小bug,需要三次crtl+z才可以停止。其他编译器都是一次即可。如下图所示。
如果两个数据都没有读入,则返回EOF,注意EOF就是-1。它和-1一模一样,只不过多了一个写法罢了。因为我们的编译器中有一个预处理指令是
#define EOF -1
如果输入的字符则会输出0.
那么说了这么多,scanf的返回值有什么用呢?
其实,我们在牛客网刷题时候,有时候题目要求多组输入,这时候我们就需要它来搭配循环使用了
- #include
- int main()
- {
- int a, b;
- while (scanf("%d %d",&a,&b) != EOF)
- {
- //代码段;
- }
- return 0;
- }
- #include<stdio.h>
- int main()
- {
- int a, b;
- while (scanf("%d %d",&a,&b) == 2)
- {
- //代码段;
- }
- return 0;
- }
以上这两种方法都可以使用,要么等于2,要么不等于EOF。来达到多组输入的目的。但是这两者还是有一点区别,像上面的代码中,要有两个数据多组输入的话,我只输入成功一个数据,一个输入失败。如果是前者的话,那就不会进入循环,直接停止多组输入,如果是后者,则继续进入多组输入,具体选择哪一种需要看具体的需求。
我们看这样一段代码
- #include
- int main()
- {
- int a;
- while (scanf("%d", &a) == 1)
- {
- printf("%d ", a);
- }
- return 0;
- }
这段代码我们运行一下,观察一下它的输入时候的过程
然后我们继续看下面这段代码
- #include<stdio.h>
- int main()
- {
- char ch = 0;
- while ((ch = getchar()) != EOF) {
- putchar(ch);
- }
- return 0;
- }
运行一下,观察输入的过程。
不知道大家看出输入有一点小区别了吗。
我们会发现,第二段代码中似乎莫名奇妙多输出了一个换行。为什么会多出一个换行呢,而scanf里面没有呢?
我们首先需要了解一下缓冲区的概念。
无论是我们的scanf还是getchar它都不会直接从我们的键盘上读取一个数据,而是先从键盘上输入到缓冲区,然后这两个函数再从缓冲区里拿数据。我们画一个图可以更加形象的演示这个过程
因为我们在输入的时候会输出一个a 和一个\n所以输入缓冲区内会有两个数据
在第二段代码中,我们的getchar会首先获取一个a,然后putchar会将其输出。 不过这之后,我们输入缓冲区还有一个\n,这个仍然会被ch获取,然后输出这个\n,也就是换行。
那到这里有人就肯定有疑问,为什么scanf不会有这个现象呢,其实scanf他是不会读入这个\n还有空格这些特殊的字符的。所以scanf在输入的时候不会出现自动换行的效果。而我们的getchar是可以读入\n还有空格的,因为getchar是获取字符的,\n和空格恰巧就是一个字符,所以可以读入。产生一些意想不到的效果。这是一个很细节的事情。很多人都会在这里犯一些经典的错误标准的零分。
比如下面就举一个经典的错误标准的零分
- #include<stdio.h>
- int main()
- {
- char password[20] = { 0 };
- int ch = 0;
- printf("请输入密码");
- //数组名本身就是地址,所以不需要&
- scanf("%s", password);
- printf("请确认密码(Y/N):>");
- ch = getchar();
- if (ch == 'Y')
- {
- printf("确认成功");
- }
- else
- {
- printf("确认失败");
- }
- return 0;
- }
这段代码运行结果为
直接提示错误。这说明有问题。问题出在哪呢,就是我们的输入缓冲区,下图是我们的运行过程图,我们将123456放进scanf中,然后缓冲区还有一个\n,我们的\n就将其放入了getchar中,这样我们的ch中就是一个换行字符,所以会出现确认失败了。
那么如何规避这种错误呢。我们可以在scanf结束后再次getchar一下来消除掉这个\n,但是这个不一定是一个好方法,因为如果我们缓冲区的字符不止一个,就处理不了了
如图所示,因为我们的scanf只拿走空格之前的或者换行之前的。所以此时缓冲区还剩下了,很多其他的字符,最后还剩一个\n。在这里谈起最后一个字符是\n。想必你也已经想到解决方法了
没错,就是使用一个循环,判断条件就是,getchar()!='\n'。里面的循环体,是一个空的,这样我们就可以清除输入缓冲区了。代码实现如下
- #include<stdio.h>
- int main()
- {
- char password[20] = { 0 };
- int ch = 0;
- printf("请输入密码");
- //数组名本身就是地址,所以不需要&
- scanf("%s", password);
- while (getchar()!='\n')
- {
- ;
- }
-
- printf("请确认密码(Y/N):>");
- ch = getchar();
- if (ch == 'Y')
- {
- printf("确认成功");
- }
- else
- {
- printf("确认失败");
- }
- return 0;
- }
我们最后再来看一段代码
- #include <stdio.h>
- int main()
- {
- char ch = '\0';
- while ((ch = getchar()) != EOF)
- {
- if (ch < '0'|| ch > '9')
- continue;
- putchar(ch);
- }
- return 0;
- }
提示:continue在while循环中的意思是:
continue是用于终止本次循环的,也就是本次循环中continue后面的代码都不会被再次执行
而是直接跳转到while语句的判断部分,进行下一次的循环入口判断。
运行结果为,只能打印,0--9之间的字符,其余全部不打印,因为即便是\n出现了输入缓冲区的作用也会因为满足判断条件,而被终止不会被打印出来
好了,本次外传就讲解这么多,主要就是这个输入缓冲区的作用,以及scanf和getchar 的返回值,以及EOF的返回值,如果本文章对你有帮助,不要忘记点赞加收藏哦。