标准输入缓冲区 stdin 使用行缓冲的方式存储输入。用户的输入数据首先被暂存在临时缓冲区中,当用户键入回车键或临时缓冲区满后,stdin 才进行 I/O 操作,将数据由临时缓冲区拷贝至 stdin 中。C语言提供的输入输出函数如 scanf 、getchar 等则从上述缓冲区 stdin 中读取数据输入。
scanf 和 getchar 等函数会在 stdin 中读取数据,若上述缓冲区中已存在数据,则直接读取其中的数据,若上述缓冲区为空,则上述函数会挂起,等待数据缓冲的完成( 用户输入回车键或数据缓冲区满后, stdin 会进行数据缓冲,之后上述函数才能继续执行)。 用户一次输入的数据可能会超过 scanf 、getchar 等函数调用所需要的数据,那么所需数据被读取后,剩余的数据仍会存放在缓冲区中,之后的函数调用会直接读取 stdin 中已有的数据。只有当缓冲区为空后,scanf 等函数才会等待用户输入(实际应该是等待 stdin 的缓冲)。
scanf:
(1)空白字符(whitespace)。scanf 会读取并忽略在 stdin 中下一个非空白字符之前的所有空白字符(空格、换行和 tab),然后读取格式化字符串中规定格式的数据,直到读取完stdin中的数据或者在遇到不匹配的数据时停止读取。
注:C语言中不能打印的字符都是空白字符,在ASCII标准表中一共有32个。加上空格字符,制表字符一共有34个。其中:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。32是空格字符。
(2)非空白字符(non whitespace)。对于格式化字符串中既非空白字符又不是格式说明符(format specifier,由%标识)的一部分的字符,scanf 会尝试从 stdin 中读取输入,并将输入与该字符比较,若匹配,则继续进行后续读取,若不匹配,则函数返回错误信息;
(3)格式说明符。以 % 开头的用于指定输入数据格式的字符。如 %d 指定需要读取一个整形,%s 需要读取一个字符串。scanf 等函数首先根据格式说明符尝试去解析 stdin 中的数据。如对于 %d ,scanf 会尝试对 stdin 中已有数据以整型的格式进行解析,若解析成功,则将上述解析结果存放到指定的内存中,若解析失败,如 stdin 中仅存在一个字符 'a',scanf 会退出并返回,但是上述不匹配的数据并不会从缓冲区中清除,后续的 scanf 调用仍从上述输入开始读取;
char ch;
scanf("%s", &ch);//abc def
此时,输入的其实是 a b c " " d e f \n,并存放至缓冲区。
但scanf在读取到" "时就会停止读取,将abc存放至ch中,scanf 函数还会在该字符串尾部加入'\0'进行存储;而后面的 d e f \n则会继续留在缓冲区中。
若想要读取空格,可以使用其他方法。
char ch;
scanf("%s", &ch);//abc
此时,输入的其实是a b c \n,并存放至缓冲区。
然后scanf对缓冲区数据进行读取,并在遇到第一个空白字符处停止,这里为 "\n" ,即读取的数据为"abc",scanf 函数还会在该字符串尾部加入'\0'进行存储;此时缓冲区还存在 \n 未被读取。
getchar:
getchar的返回值为int类型。当程序调用getchar时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int ch = 0;
ch = getchar();
return 0;
}
因此注意在使用getchar时,用int类型的变量接收。
getchar 从标准输入中读取数据,而 stdin 是采用行缓冲的方式记录用户输入,也就是只有当用户键入回车键或输入至缓冲区末尾时,才会读取一个字符。这样用户可以一次输入不止一个字符,读取过后缓冲区可能不为空。当再次调用 getchar 时,若缓冲区不为空,getchar 就会直接读取在缓冲区中字符,而不是等待用户输入。可以认为是getchar 等待的是行缓冲的完成,而不是用户输入的完成,在行缓冲完成后,只要缓冲区不为空,getchar 就可以读取字符,而不需要等待用户输入
scanf与getchar缓冲区例子
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//模拟用户输入密码,若正确,则输入Y确认,并提示输入正确
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);//假设为123456,输入的其实是123456\n,此时缓冲区还有\n未处理
//清理缓冲区里的\n
int box = 0;
while ((box = getchar()) != '\n')
{
;
}
printf("请确认密码:");
int ch = getchar();
if ('Y' == ch)
{
printf("确认成功:(Y/N)");
}
else
{
printf("确认失败");
}
return 0;
}