当字符遇上 scanf() 要当心

news/2024/10/2 10:38:21

当字符遇上 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);

运行结果如下:
image

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

这里有两个问题

  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);

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/29421.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

瞬回丝滑!30秒解决win11文件管理器卡顿问题!

命令文本:reg add "HKCU\Software\Classes\CLSID\{d93ed569-3b3e-4bff-8355-3c44f6a52bb5}\InprocServer32" /f /ve 如需取消输入这个命令 reg delete "HKCU\Software\Classes\CLSID\{d93ed569-3b3e-4bff-8355-3c44f6a52bb5}" /f

k8s资源编排-yml介绍

yaml文件: 叫做“资源清单文件”,通过kubecl命令直接使用yaml文件就可以实现对大量的资源对象进行编排部署了。2.Yaml的组成部分 yaml由2个部分组成: 2.1 控制器定义 2.2 被控制对象 yaml文件自己写比较繁琐,还好有自动生成yml格式文件的方式: 1.使用kubectl create命令生…

React文本溢出组件封装以及高亮提示

React文本溢出组件封装以及高亮提示Abbr 组件:使用场景:当我们需要设置支持最大行数时进行省略展示 当我们需要设置支持设置超过多少字符进行省略展示 当我们需要设置支持关键字高亮展示(有点问题,当关键字被裁剪成...之后,就无法高亮) 当我们需要支持忽略大小写高亮 当我…

AlmaLinux 9.4 正式版发布 - RHEL 二进制兼容免费发行版

AlmaLinux 9.4 正式版发布 - RHEL 二进制兼容免费发行版AlmaLinux 9.4 正式版发布 - RHEL 二进制兼容免费发行版 由社区提供的免费 Linux 操作系统,RHEL 二进制兼容发行版 请访问原文链接:AlmaLinux 9.4 正式版发布 - RHEL 二进制兼容免费发行版,查看最新版。原创作品,转载…

cc6链:绕过cc1的JDK版本限制

这里回到LazyMap,LazyMap的get方法可以触发后续的rce为什么cc1有jdk版本限制 JDK中的AnnotationInvocationHandler的readObject更新了,所以cc1用不了 但是前面的部分还是存在的,只要我们找到一个新的入口就还是能执行命令 这里回到LazyMap,LazyMap的get方法可以触发后续的r…

第二届数信杯南区wp-easyJava

第二届数信杯南区easyJavawriteup easyJava 用Eclipse Memory Analyzer进行分析,利用OQL查找字符串这里要写正则表达式:我写了\\u.*意思是找unicode字符串,因为这里的中文都做了unicode编码搜索到这么一个字符串列表,转码——红色框框里的是还原后的内容。如下: 想跟你说一…