“C语言“——scanf()、getchar() 、putchar()、之间的关系

scanf函数说明

scanf函数是对来自于标准输入流的输入数据作格式转换,并将转换结果保存至format后面的实参所指向的对象。

而const char*format 指向的字符串为格式控制字符串,它指定了可输入的字符串以及赋值时转换方法。

简单来说给一个打印格式(输入数据格式),scanf函数会将打印数据格式的结果转换放到后面的实参变量当中。

比如下面的代码

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 0;
	char ch = 0;
	double b = 0.0;
	scanf("%d %c %lf", &a, &ch, &b);
	printf("%d %c %lf", a, ch, b);

	return 0;
}

但scanf函数有一个缺点,就是scanf读取不了空格和回车等字符,当scanf读到这两个字符时,scanf会读取失败返回EOF,如果匹配错误也会读取失败返回EOF(比如应该是%d的数据转换到了char实参变量当中)。

EOF

EOF全名是:End of File 在<stdio.h>头文件中被定义为负值,即-1。

EOF的值不同编译器下值不同,在VS2019是-1

如果没有将头文件<stdio.h>包含到程序中,那么EOF就没有定义,程序不能编译和运行。

getchar和putchar

putchar的返回值是int,因为字符在程序存储的是ASCII码值,而且putchar只能打印字符,不能打印字符串,就是配合getchar()使用的。如果putchar成功获取了字符就返回所写字符,失败就返回EOF。

cpp 复制代码
#include<stdio.h>
int main()
{
	putchar('a');
	putchar('b');
	putchar('c');
	return 0;
}

getchar返回也是int,参数是void,可有可无,getchar是从标准输入流读取字符(空格 回车都可以读取)并将其返回,如果读取失败就返回EOF。

比如

cpp 复制代码
#include<stdio.h>
int main()
{
	int ch;
	while ((ch = getchar()) != EOF)
	{
		putchar(ch);
	}
	return 0;
}

getchar如果缓冲区没有字符,getchar会等待我们的输入且摁下回车键才会打印在屏幕上。

配合putchar使用。

按下回车键后才在屏幕上输出对应字符。

ctrl+Z是将程序结束掉,退出了程序终止了代码循环。

crtl+Z相当于EOF。

为什么按回车键才会将字符输出到屏幕上呢?

C语言的输入输出一般会将读入的字符以及待输出的字符暂时保存在缓存中(缓冲区),当达到以下条件才进行实际的输入输出操作。

1.缓存已满 2.输入换行符(\n) 3.立即输出

对应方式称为1.全缓冲 2.行缓冲 3,无缓冲

键盘输入的内容不会直接给cpu处理打印在屏幕上 而是交给缓存 提高cpu运行效率。

getchar与scanf的关系

我们根据下图代码来分析getchar和scanf之间的关系

cpp 复制代码
#include<stdio.h>
int main()
{
	char password[20] = { 0 };
	printf("请输入密码:>");
	scanf("%s", password);
	printf("请确认密码正确Y/N:>");
	char ch = 0;
	scanf("%c", &ch);
	if ('Y' == ch)
	{
		printf("密码正确\n");
	}
	else
	{
		printf("密码错误\n");
	}
	return 0;
}

当程序运行时,我们刚输入完密码程序直接结束了,都没有确定密码的正确性,这是为什么呢?

当我们输入密码摁下回车键时,在缓冲区输入了1234\n,回车键相当于换行等于转义字符\n

第一个scanf读取了1234 第二个scanf读取了\n 直接填满了两个scanf的嘴巴,因此直接程序结束

并且结果为密码错误,当我们输入完1234摁下回车键那刻\n也被载入到了缓冲区,第二个scanf

定睛一看缓冲区有内容直接就读取走了。

解决办法

这时我们就要请出getchar给scanf擦屁股了,因为getchar能读取回车 空格等字符。

cpp 复制代码
#include<stdio.h>
int main()
{
	char password[20] = { 0 };
	printf("请输入密码:>");
	scanf("%s", password);
	printf("请确认密码正确Y/N:>");
	char ch = 0;
	getchar();
	scanf("%c", &ch);
	if ('Y' == ch)
	{
		printf("密码正确\n");
	}
	else
	{
		printf("密码错误\n");
	}
	return 0;
}

但如果有人在设置密码时写入了空格,而且scanf读取不了空格和回车字符,这时还是会出现代码判断错误导致运行结果错误。

我们直接用while循环搞一个一劳永逸的解决方案

直接使用循环清空缓冲区的空格 回车等字符

cpp 复制代码
#include<stdio.h>
int main()
{
	char password[20] = { 0 };
	printf("请输入密码:>");
	scanf("%s", password);
	printf("请确认密码正确Y/N:>");
	char ch = 0;
	while ((getchar()) != '\n')
	{
		;
	}
//以下两种写法都ok
	scanf("%c",&ch);
    //ch=getchar();
	if ('Y' == ch)
	{
		printf("密码正确\n");
	}
	else
	{
		printf("密码错误\n");
	}
	return 0;
}

关系

输入b后摁下回车键形成\n

缓冲区载入的就是b\n

第一个scanf读取b 第二个scanf读取\n 因此程序结束语在第三行

输入a\n

scanf读取a getchar读取\n putchar输出a

输入a\n 第一个scanf读走a getchar读走\n

第二个scanf正常使用再次输入b\n putchar在下一行输出b

scanf读走as \nbxx是putchar读取的 a是第二个scanf读取的后面还有个\n

所以程序结束语在第4行。

总结

如果scanf输入过程中遇到空格 回车键等字符,scanf自己处理不了的读取字符时,就用getchar来解决,如果有多个处理不了的特殊字符就用while循环来处理(scanf处理不了的空格 回车键等特殊字符),配合getchar和putchar来给scanf的正常使用(擦屁股)。

相关推荐
cici158748 分钟前
非线性模型预测控制(NMPC)基于CasADi的MATLAB实现
开发语言·matlab
独特的螺狮粉20 分钟前
开源鸿蒙跨平台Flutter开发:量子态波函数坍缩系统-波动力学与概率云渲染架构
开发语言·flutter·华为·架构·开源·harmonyos
冰暮流星29 分钟前
javascript之dom访问属性
开发语言·javascript·dubbo
lsx20240629 分钟前
SQL Auto Increment 自动增长
开发语言
t1987512831 分钟前
MATLAB模糊数学模型(Fuzzy Mathematical Model)实现指南
开发语言·matlab
Evand J40 分钟前
MATLAB批量保存现有绘图窗口的方法,简易方法,直接保存到当前目录,不手动设置
开发语言·matlab·教程
忽而今夏&_&40 分钟前
python 刷题最基础的一些
开发语言·python
quintin-lee43 分钟前
Postgres 内核:从入门到“入土” (三) —— Page 结构:数据是如何在磁盘上“躺平”的
c语言·数据库·postgresql·数据库架构
前端郭德纲1 小时前
JavaScript 原型相关属性详解
开发语言·javascript·原型模式
于先生吖1 小时前
基于 SpringBoot 架构,高性能 JAVA 动漫短剧系统源码
java·开发语言·spring boot