当字符遇上 scanf() 要当心

看一下程序

    char ch1,ch2;
printf("请输入ch1,ch2的值:");
scanf("%c %c",&ch1,&ch2);
printf("ch1 = %c, ch2 = %c\n",ch1,ch2);
printf("请再次输入ch1的值:");
scanf("%c",&ch1);
printf("请再次输入ch2的值:");
scanf("%c",&ch2);
printf("ch1 = %c, ch2 = %c\n",ch1,ch2);

运行结果如下:

第二次输出为什么会出现异常情况呢 ?

这里有两个问题

  1. 为什么出现了 请再次输入ch1的值:请再次输入ch2的值:的情况,按照原本的预测请应该是先出现请再次输入ch1的值:,键盘输入了一个字符之后再出现请再次输入ch2的值:,键盘再输入一个字符
  2. 为什么第二次输入ch1,的值后,直接就输出了ch1 和ch2 ,而且 ch1 = '\n', ch2 = 'C'?

第一个问题

​ 首先需要知道CPU 的工作原理

由于输入设备和输出设备对于数据的读写速度比较慢,CPU为了降低输入输出次数,提高运行效率,避免长时间的等待,所以内核就在内存中提供了一块空间作为缓冲区,( 缓冲区也可以称为缓存(Cache),是属于内存空间的一部分。)

每当使用读操作函数,试图将数据读取出来时,数据都会流过标准*输入缓冲区*,然后再在适当的时刻冲洗(或称刷新,flush)到内核缓冲区,最后才真正得到数据。

所以CPU 并不是没每时每刻都在读取数据,而是在满足刷新条件时才会从出入缓冲区读取数据。刷新条件按形式分为三种:全缓冲、行缓冲、无缓冲。

全缓冲:指的是当缓冲区被填满就立即把数据冲刷到文件、或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件,一般读写文件的时候会采用

无缓冲:指的是没有缓冲区,直接输出,一般linux系统的标准出错stderr就是采用无缓冲,这样可以把错误信息直接输出。

行缓冲:指的是当缓冲区被填满(一般缓冲区为4KB,就是4096字节)或者缓冲区中遇到换行符’\n’时,或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件中,一般操作IO设备时会采用,比如printf函数就是采用行缓冲。

知道了CPU 工作原理之后,我们就知道了printf() 函数的刷新条件有四个:

  1. 程序结束;
  2. 遇到换行 ‘\n’;
  3. 缓冲区满;
  4. 利用函数fflush() 手动刷新.

这也就解释了为什么出现 请再次输入ch1的值:请再次输入ch2的值:的情况,是因为没有遇到刷新条件,所以在写程序时,非必要情况都需要加上换行‘\n’。

第二个问题

​ 在解释CPU 工作原理是说过 Linux 系统读取数据,是先从键盘进入到输入缓冲区,再进入到内核缓冲区,最后读取到数据。所以在第一次输入ch1 ,ch2 的值为A B<手动输入'\n' ,缓冲区应该是字符串

"A B\n", CPU 先读取“A B”,光标位置在字符B 的后面,当再次从缓冲区读取字符型数据时读出来的是字符‘\n’ ,ch1 = '\n', ch2 读取的是后面输入的字母C 。

那么我们如何解决这个问题呢?

​ 在读取字符之前先要清空缓冲区,使用函数getchar() ,让光标后移到正确的位置,如下:

    char ch1,ch2;
printf("请输入ch1,ch2的值:");
scanf("%c %c",&ch1,&ch2);
printf("ch1 = %c, ch2 = %c\n",ch1,ch2);
getchar(); //读取换行符
printf("请再次输入ch1的值:");
scanf("%c",&ch1);
getchar(); //读取换行符
printf("请再次输入ch2的值:");
scanf("%c",&ch2);
printf("ch1 = %c, ch2 = %c\n",ch1,ch2);

当字符遇上 scanf() 要当心的更多相关文章

  1. 前端遇上Go: 静态资源增量更新的新实践

    前端遇上Go: 静态资源增量更新的新实践https://mp.weixin.qq.com/s/hCqQW1F8FngPPGZAisAWUg 前端遇上Go: 静态资源增量更新的新实践 原创: 洋河 美团 ...

  2. DP在字符匹配上的实现

    在此保存下近段时间做的DP在字符匹配上的实现的题目 对于不同的字符串来说,2者只能不断将下标往后推移来实现匹配从而得到的最大匹配数 如 abcd 和 dcba 这个最大匹配数只能为1,因为两个d匹配后 ...

  3. html实体编码遇上js代码

    单双引号 在js代码中 在js中单.双引号引起来的是字符串,如果我们要在字符串中使用单.双引号,需要反斜杠进行转义 let str='user\'s name'; // or let str=&quo ...

  4. MVC遇上bootstrap后的ajax表单模型验证

    MVC遇上bootstrap后的ajax表单验证 使用bootstrap后他由他自带的样式has-error,想要使用它就会比较麻烦,往常使用jqueyr.validate的话只有使用他自己的样式了, ...

  5. 敏捷遇上UML-需求分析及软件设计最佳实践(郑州站 2014-6-7)

      邀请函: 尊敬的阁下:我们将在郑州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实 ...

  6. 敏捷遇上UML—软创基地马年大会(广州站 2014-4-19)

        我们将在广州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战技巧. 时间:2 ...

  7. 敏捷遇上UML——软创基地马年大会(深圳站 2014-3-15)

    邀请函: 尊敬的阁下: 我们将在深圳为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战 ...

  8. 初识genymotion安装遇上的VirtualBox问题

    想必做过Android开发的都讨厌那慢如蜗牛的 eclipse原生Android模拟器吧! 光是启动这个模拟器都得花上两三分钟,慢慢的用起来手机来调试,但那毕竟不是长久之计,也确实不方便,后来知道了g ...

  9. SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案

    SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...

  10. 当创业遇上O2O,新一批死亡名单,看完震惊了!

    当创业遇上O2O,故事就开始了,总投入1.6亿.半年开7家便利店.会员猛增至10万……2015半年过去后,很多故事在后面变成了一场创业“事故”,是模式错误还是烧钱过度?这些项目的失败能给国内创业者带来 ...

随机推荐

  1. 如何拿到接口返回的消耗token

    SemanticKernel 以下引用自官方案例 Text模型 使用Kernel FunctionResult functionResult = await kernel.InvokePromptAs ...

  2. #点分治,Kruskal#AT3611 Tree MST

    题目 给定一棵 \(n\) 个节点的树,现有有一张完全图, 两点 \(x,y\) 之间的边长为 \(w_x+w_y+dis_{x,y}\), 其中 \(dis\) 表示树上两点的距离. 求完全图的最小 ...

  3. Response下载文件

    Response下载文件的主要步骤有七个: 1.要获取下载文件的路径 String realPath = "F:\\JavaWeb\\WorkSpace\\JavaWeb-Servlet\\ ...

  4. leetcode-数组中两元素的最大乘积

    题目描述 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值. 请你计算并返回该式的最大值. 示例 1: 输入:nums ...

  5. HarmonyOS 3百机升级计划,来了!

    HarmonyOS 3规模升级来了! 为大家奉上百余款机型升级计划! 你的手机什么时候可以升级? 赶快下滑查看!

  6. centos-stream-9 centos9 配置国内yum源 阿里云源

    源配置 tips: yum配置文件路径 /etc/yum.repos.d/centos.repo 1.备份源配置 [Very Important!] mv /etc/yum.repos.d/cento ...

  7. 如何在macOS上安装Python3并设置本地编程环境

    介绍 Python是一种多功能编程语言,可用于许多不同的编程项目.1991年首次出版,其名称灵感来自英国喜剧组织Monty Python,开发团队希望使Python成为一种有趣的语言.易于设置,并以相 ...

  8. redis 简单整理——redis 准备篇[一]

    前言 简单整理一下redis. 正文 为什么使用redis? 速度快 1.1 内存执行 1.2 c语言编写,速度相对快一些 1.3 单线程,比较符合这种存储模式 2 丰富的数据结构 3 丰富的功能机制 ...

  9. c# mvc action 跳转方式

    前言 c# 的mvc 有很多的action跳转方式,在此总结一下具体的跳转方法. 这里不用去记,而是知道能怎么做,为什么能这样做,重在思考. 正文 一.RedirectToAction("I ...

  10. Docker安装使用--Centos

    目录 前言 Docker安装使用 安装步骤 卸载旧版本 安装utils 配置utils的存储库 安装Docker 安装指定版本Docker 验证安装 官网安装说明 阿里云镜像加速 Docker使用 帮 ...