详细聊聊c语言中的缓冲区问题

目录
  • 发现问题
  • 例题
  • 问题原因
  • 解决方法一:
  • 解决方法二:
  • 解决方案三:
  • 出错二
  • gets函数引入
  • 为什么要引入缓冲区
  • 总结

发现问题

你是不是总会出现当你输入的时候(你想的是只输出一个内容),但是最后却输入两个。

比如下面这个例子

 那这到底是是哪出了问题呢?

没错这就是关于缓冲区的问题。

我们先仔细了解这个题目

例题

判断字母是否为元音字母包括大小写。

看代码实现(错误的)

#include<stdio.h>
int main()
{
	int i = 0;
	char ch = 0;
	char yyzm[20] = { 'a','A','e','E','i','I','o','O','u','U' };
	while(scanf("%c", &ch)!=EOF)
	{
		for (i = 0; i < 10; i++)
		{
			if (ch == yyzm[i])
			{
				printf("元音字母\n");
				break;
			}
		}
		if (i == 10)
		{
			printf("辅音字母\n");
		}
	}

	return 0;
}

问题原因

我们一般怎么输入呢?

我们先输入元音字母o然后在按一下回车,一般输入都是这样输入的到底是哪出了问题呢?

没错就是那个回车惹的祸。每当我们输入一个字母的时候,scanf读取字母之后,就会放入缓冲区中,回车一下当然也会放个'\n'字符也就是空格,当计算机拿取字符的时候先拿走一个字符,接着看里面还有没有字符,如果有字符就会继续读取,如果没有则进行下面的内容。

在我们这个代中由于是多次输入数据,就会读入字符后第一个if语句结束,如果还有字符的话,计算机就会继续拿字符,这时就拿了一个'\n','\n'不是元音字母就会进入下一个if语句输出。

那我们如何解决呢?

解决方法一:

在后面加入getchar(),它的作用就是清理缓存区,由于输入字符,计算机是一个一个字符读取的,又因为我们多次输入,所以getchar总是会读取那个'\n';

解决方法二:

我们在scanf%c后面加个'\n',由于是一个一个读取字符的,如果后面有'\n',就会把\n也拿走。

解决方案三:

在%c前面加个空格,这样做的目的是每次读取下一个字符时,就会把上一个字符后面的'\n'清理掉。

正确的代码:

#include<stdio.h>
int main()
{
	int i = 0;
	char ch = 0;
	char yyzm[20] = { 'a','A','e','E','i','I','o','O','u','U' };
	while(scanf(" %c", &ch)!=EOF)//可以在%c后面加个'\n',也可以在%c前面加个空格,目的是清理缓冲区
	{
		for (i = 0; i < 10; i++)
		{
			if (ch == yyzm[i])
			{
				printf("元音字母\n");
				break;
			}
		}
		if (i == 10)
		{
			printf("辅音字母\n");
		}
	}
	//getchar();清理缓冲区
	return 0;
}

出错二

当我们用scanf输入字符串的时候,如果遇到空格也会出现问题,这时我们就可以引入另外一个函数那就是gets函数

gets函数引入

gets函数的优点与scanf对比:

gets() 函数不仅比 scanf 简洁,而且,就算输入的字符串中有空格也可以直接输入,不用像 scanf 那样要定义多个字符数组。

关于使用 gets() 函数需要注意:使用 gets() 时,系统会将最后“敲”的换行符从缓冲区中取出来,然后丢弃,所以缓冲区中不会遗留换行符。这就意味着,如果前面使用过 gets(),而后面又要从键盘给字符变量赋值的话就不需要吸收回车清空缓冲区了,因为缓冲区的回车已经被 gets() 取出来扔掉了。(此段话是在网上查到的,整理为复习准备,请见谅)。

我们来做个题吧

逆序字符串

#include<stdio.h>
#include<string.h>
void swap(char* str)
{
    int i = 0;
    int len = strlen(str);
    for (i = 0; i < len / 2; i++)
    {
        char tmp = 0;
        tmp = str[i];
        str[i] = str[len - i - 1];
        str[len - i - 1] = tmp;
    }
    printf("%s", str);
}
int main()
{
    //逆序字符串的内容
    char str[100];
    int i = 0;
    gets(str);
    swap(str);

    return 0;
}

为什么要引入缓冲区

比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。

又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。

现在您基本明白了吧,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。 

缓冲区的类型

缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。

1) 全缓冲

在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。

2) 行缓冲

在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。

3) 不带缓冲

也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

总结

(0)

相关推荐

  • 详解C语言之缓冲区溢出

    一.缓冲区溢出原理 栈帧结构的引入为高级语言中实现函数或过程调用提供直接的硬件支持,但由于将函数返回地址这样的重要数据保存在程序员可见的堆栈中,因此也给系统安全带来隐患.若将函数返回地址修改为指向一段精心安排的恶意代码,则可达到危害系统安全的目的.此外,堆栈的正确恢复依赖于压栈的EBP值的正确性,但EBP域邻近局部变量,若编程中有意无意地通过局部变量的地址偏移窜改EBP值,则程序的行为将变得非常危险. 由于C/C++语言没有数组越界检查机制,当向局部数组缓冲区里写入的数据超过为其分配的大小时,就

  • C语言中输入输出流与缓冲区的深入讲解

    前言 缓冲区 又称为缓存,它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区. 缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区. 原理介绍: 当调用输入函数scanf()时,输入函数会将我们输入的数字输入到输入缓冲区, 而当我们的输入缓冲区有内容时,再次输入将不会被执行, 而是直接跳过执行,将输入缓冲区的内容赋给变量; 1.为什么要引入缓冲区 例如,我们从磁盘里取信息,我们先把读出的数据放在缓

  • 详细聊聊c语言中的缓冲区问题

    目录 发现问题 例题 问题原因 解决方法一: 解决方法二: 解决方案三: 出错二 gets函数引入 为什么要引入缓冲区 总结 发现问题 你是不是总会出现当你输入的时候(你想的是只输出一个内容),但是最后却输入两个. 比如下面这个例子  那这到底是是哪出了问题呢? 没错这就是关于缓冲区的问题. 我们先仔细了解这个题目 例题 判断字母是否为元音字母包括大小写. 看代码实现(错误的) #include<stdio.h> int main() { int i = 0; char ch = 0; cha

  • 一起聊聊Go语言中的语法糖的使用

    目录 前言 进入正题 可变长参数 声明不定长数组 ... 操作符 切片循环 忽略变量.字段或者导包 短变量声明 另类的返回值 总结 前言 由于工作变动,我现在已经开始使用Golang了.用了一段时间之后,我发现Golang(后面简称Go)中的语法糖还蛮多的,有些语法糖还让会让人很懵逼.那么接下来,让我以一个曾经的 Java CURD boy,来说一说 Go 中的语法糖. 进入正题 至于什么是语法糖,名词解释我就不解释了,老司机自然是懂,新手司机想了解的可以去百度问一下.闲话少说我们直接开讲. 可

  • 详细解析C语言中的开方实现

    关于C语言中的开方计算,首先想到的当然是sqrt()函数,让我们先来回顾一下它的基本用法: 头文件:#include <math.h> sqrt() 用来求给定值的平方根,其原型为: double sqrt(double x); 参数 x 为要计算平方根的值. 如果 x < 0,将会导致 domain error 错误,并把全局变量 errno 的值为设置为 EDOM. 返回值 返回 x 平方根. 注意,使用 GCC 编译时请加入-lm. 实例计算200 的平方根值. #include

  • 详细谈谈C语言中动态内存

    目录 前言 1.关于动态内存的函数 1.1malloc和free函数 1.2calloc函数 1.3realloc函数 2.常见的动态内存错误 2.1对NULL指针解引用 2.2对动态内存开辟的空间越界访问 2.3 对非动态开辟内存使用free释放 2.4 使用free释放一块动态开辟内存的一部分 2.5对同一块动态内存多次释放 2.6内存泄漏 补充:为什么要引入动态内存分配 总结 前言 关于动态内存管理,可能有学习过的小伙伴,也有没有听说过的.没有听说过的小伙伴会觉得很奇怪啊,为什么要动态开辟

  • 详细介绍Python语言中的按位运算符

    按位运算符是把数字看作二进制来进行计算的.Python中的按位运算法则如下: 按位与   ( bitwise and of x and y ) &  举例: 5&3 = 1  解释: 101  11 相同位仅为个位1 ,故结果为 1 按位或   ( bitwise or of x and y ) |  举例: 5|3 = 7  解释: 101  11 出现1的位是 1 1 1,故结果为 111 按位异或 ( bitwise exclusive or of x and y ) ^  举例:

  • 详细聊聊Vue.js中的MVVM

    目录 MVVM的理解 MVVM的原理 脏检查机制: 数据劫持 相同点 实现MVVM 总结 MVVM的理解 MVVM拆开来即为Model-View-ViewModel,有View,ViewModel,Model三部分组成.View层代表的是视图.模版,负责将数据模型转化为UI展现出来.Model层代表的是模型.数据,可以在Model层中定义数据修改和操作的业务逻辑.ViewModel层连接Model和View. 在MVVM的架构下,View层和Model层并没有直接联系,而是通过ViewModel

  • C语言中的操作符优先级的详细介绍

    C语言中的操作符优先级的详细介绍 C语言中操作符的优先级大全, 当然c++, Objective-C,大部分语言都试用. 下面是来自The C Programming Language 2th的总结. OperatorsAssociativity(结合性) 1. () [] -> . 左->右 2. ! ~ ++ -- + - *(type)sizeof 右->左 3. * / % 左->右 4. + - 左->右 5. << >> 左->右 6

  • C语言中的字符(char)详细讲解

    1.字符型(char)简介 字符型(char)用于储存字符(character),如英文字母或标点. 严格来说,char 其实也是整数类型(integer type),因为 char 类型储存的实际上是整数,而不是字符. 计算机使用特定的整数编码来表示特定的字符. 2. 声明字符型变量 3. 字符常量与初始化 实例: 用 char 类型来专门表示一个字符,例如: char a='1'; char b='$'; char c='X'; char d=' '; // 空格也是一个字符 char e=

  • C语言中基础小问题详细介绍

    1.printf格式输出函数 如果格式控制说明项数多于输出表列个数,则会输出错误数据:如果输出表列个数多于格式控制说明数,则多出数不被输出.%md,m指的是输出字段的宽度.如果输出字段位数小于m,则左端以空格补齐,若大于m,则按照实际位数输出.%-md,基本同上,只不过不同之处在于,空格在右端补齐printf参数可以是常量,变量或表达式,VC++ 6.0中采用从右向左顺序求值,从左向右输出如 复制代码 代码如下: int x = 5; printf("%4d%4d%4d", x, ++

  • C语言中左移和右移运算符详细介绍

    C语言中左移和右移运算符详细介绍 左移运算符(<<) 左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负值),其右边空出的位用0填补,高位左移溢出则舍弃该高位. 右移运算符(>>) 右移运算符是用来将一个数的各二进制位右移若干位,移动的位数由右操作数指定(右操作数必须是非负值),移到右端的低位被舍弃,对于无符号数,高位补0. 对于有符号数,某些机器将对左边空出的部分用符号位填补(即"算术移位"),而另一些机器则对左边空出

随机推荐