当字符遇上 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. 数据解析之re正则表达式

    数据解析之re正则表达式 1. 正则基础 1.1 为什么使用正则 需求 判断一个字符串是否是手机号 解决 编写一个函数,给函数一个字符串,如果是手机号则返回True,否则返回False 代码 def ...

  2. CSP2019-S2总结

    目录 前言 洛谷 5657 格雷码 代码(找规律) 洛谷 5658 括号树 分析 代码 洛谷 5659 树上的数 洛谷 5664 Emiya 家今天的饭 洛谷 5665 划分 分析 代码 洛谷 566 ...

  3. Avalonia的自定义用户组件

    Avalonia中的自定义用户控件 Avalonia是一个跨平台的.NET UI框架,它允许开发者使用C#和XAML来构建丰富的桌面应用程序. 自定义用户控件(UserControl)是Avaloni ...

  4. C#中yield return的作用

    C#中yield return的作用 yield return作用在 return 时,保存当前函数的状态,下次调用时继续从当前位置处理.示例说明如下代码所示,主函数使用 foreach 输出 Get ...

  5. elasticsearch映射创建查询 和Spring Data ElasticSearch入门

    Elasticsearch核心概念 Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document).然而它不仅 仅是存储,还会索引( ...

  6. CentOS 6.5 LAMP分主机平台的搭建及测试

    CentOS 6.5 LAMP分主机平台的搭建及测试 看似非常之完备,转来抽空测试学习 原文地址:http://www.it165.net/os/html/201403/7595.html 前言   ...

  7. RestfulApi 学习笔记——内容协商(三)

    前言 什么是内容协商呢?是这样的,我们在请求的时候都有两个属性,一个是Content-Type,另一个是accept,这两个什么意思呢? content-type 这个是表示自己传输的是什么内容,就像 ...

  8. c# TryParse

    c# tryparse和parse对比,大体内容是一致的. parse 会返回异常,主要表现为三种. 第一种ArgumentNullException,这里面表示,为空. 第二种formatexcep ...

  9. xilinx下载器,JTAG-HS3和Platform Cable USB II 速度对比

    下面测试速度,以一个V7的配置文件为例子.文件大小如下,27MB.特别是对于有点规模的项目配置文件都是很大的.总不能是点灯项目. 选择普通的下载器,Platform Cable USB.这种下载器是基 ...

  10. 介绍一个气缸控制的FB程序块

    关键词: 气缸,双控.单控.电磁阀.感应器.初始位置(简称"始位").末端位置(简称"端位").屏蔽功能.延时功能.报警功能 正文: 1.为什么要做气缸FB功能 ...