【C语言航路外传】输入输出函数及输入缓冲区的那点事

简介: 【C语言航路外传】输入输出函数及输入缓冲区的那点事

一、getchar,putchar的使用

当我们想要在输入一个字符,并且打印它的时候,我们头脑中最先想到的一定是scanf和printf,这两个函数搭配使用来输出并且打印。

#include<stdio.h>
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和scanf函数的返回值

1.getchar的返回值

我们来了解一下getchar这个函数,我们先进入一个网站,这个网站里面有着各种库函数的使用详解。c语言库函数,在这个库函数网站里面,我们在上面搜索getchar

搜索之后我们进入如下界面

在这里,我们会发现,int getchar (void),也就是说,getchar接收的参数是void,也就是空的意思,即不需要参数,然后返回一个int类型的整型数字,这个int的类型的整型数字就是从我们键盘上读取到的那个数字的ASCII值。它还是一个只获取一个字符类型的函数。但是如果获取失败,他就返回EOF,注意EOF就是-1。

2.scanf的返回值

对于scanf我们也同样了解一下。

成功时,函数返回成功填充的参数列表的项数。由于匹配失败、读取错误或到达文件末尾,此计数可以与预期的项数匹配,也可以更少(甚至为零)。

如果在读取过程中发生读取错误或到达文件末尾,则设置适当的指示符(feof或ferror)。并且,如果在成功读取任何数据之前发生任何一种情况,则返回EOF。

如果在解释宽字符时发生编码错误,该函数将errno设置为EILSEQ。

这是scanf的返回值,也就是说,scanf的返回值等于scanf成功读取到数据的个数。如果失败,则返回EOF,即-1。

举个例子,请看下面一段代码。

#include<stdio.h>
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<stdio.h>
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<stdio.h>
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的返回值,如果本文章对你有帮助,不要忘记点赞加收藏哦。

相关文章
|
2月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
73 23
|
6天前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
11 1
一文彻底搞清楚C语言的函数
|
2月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
65 24
|
2月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
75 16
|
2月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
42 3
|
2月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
27 2
|
2月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
56 1
|
C语言 C++ 编译器
C语言清空输入缓冲区
来源:http://blog.csdn.net/guanyasu/article/details/53153705 https://zhidao.baidu.com/question/5241738.html   C语言中如何清空输入输出缓冲区  上述描述似乎能够解决问题了,但是,fflush( )并不是标准C语言库函数,只是部分编译器自己实现的函数,是对标准C的扩展。
1616 0
|
2月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
78 15
|
3月前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
103 10