1.4 输入缓冲区相关的笔记

文章目录

    • I\O的缓冲类型对应的Flag(编译器)
    • 缓冲类型的解释(全缓冲 行缓冲 无缓冲)
    • 读取字符串时希望读入空格“Hello world”
    • 对scanf(格式化输入)的理解
      • 一些刁钻的例子
      • 总的来说
    • getchar函数
    • 等待用户输入的字符输入 getch与getche
    • printf


I\O的缓冲类型对应的Flag(编译器)

#Linux:

//stdout :_IOLBF 行缓冲

//stderr:_IONBF 无缓冲

//fopen:_IOFBF 全缓冲

#MSVC:

//stdout :_IONBF 无缓冲

//stderr:_IONBF 无缓冲

//fopen:_IOFBF 全缓冲

缓冲类型的解释(全缓冲 行缓冲 无缓冲)

  • 全缓冲:只有当划定的缓冲区被填满或者数据读取至末尾时,才开始执行 I\O 操作(执行系统提供的 read\write 操作)。磁盘文件的读写一般采用这种方式。

  • 行缓冲:当输入输出过程遇到换行符’'\n"或者当分配缓冲区已满时,才开始执行 I\O 操作。一般涉及终端的读写操作如 stdin 与 stdout 使用这种缓冲方式。

  • 无缓冲:当有数据产生时,马上由相应的设备进行处理。一般来说 stderr(standard error) 使用这种缓冲方式,使得有错误信息时马上能够得到响应。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存

注意,以上关于 stdin/stdout 的缓冲方式并不是直接规定死的。一些语言的语言规范会对缓冲实现给出一定的限制,但并不具体,只是许多标准I/O是以上述方式实现的而已。

读取字符串时希望读入空格“Hello world”

用gets函数可以连空格一起读入。

对scanf(格式化输入)的理解

函数声明:int scanf( format string , arg1 , arg2 , …);

从函数声明可以看到,scanf 的参数由指示读取动作的格式化字符串( format string )和相应的地址参数 arg1…argn 组成。scanf 函数将输入从标准输入缓冲区 stdin 中读入,并将它们以格式化字符串中指定的格式存储到额外的参数 arg1…arg2 等指定的内存空间中。其中额外的参数(additional argument)指向的内存空间的数据类型应该与格式化字符串中指定的数据类型相一致。

  1. 空白字符(whitespace)scanf 会读取并忽略在 stdin 中下一个非空白字符之前的所有空白字符(空格、换行和 tab)不一定全会跳过,例子在这,然后读取格式化字符串中规定格式的数据。
    若格式化字符串中包含空白字符,则该空白字符会与输入缓冲区中任意数量的连续空白字符相匹配,并将其从缓冲区中清除(包括0个)。活学活用——希望忽略输入之前的空白字符如何实现
    例如格式化字符串"%d %d",会要求 scanf 首先从缓冲区中读取一个整型(若之前存在空白字符则跳过),再跳过输入缓冲区中连续的空白字符(与格式化字符串中的空白字符匹配),最后再读取一个整形;

  2. 非空白字符(non whitespace)。对于格式化字符串中既非空白字符又不是格式说明符(format specifier,由%标识)的一部分的字符,scanf 会尝试从 stdin 中读取输入,并将输入与该字符比较,若匹配,则继续进行后续读取,若不匹配,则函数返回错误信息;

  3. 格式说明符。以 % 开头的用于指定输入数据格式的字符。如 %d 指定需要读取一个整形,%s 需要读取一个字符串。scanf 等函数首先根据格式说明符尝试去解析 stdin 中的数据,如对于 %d ,scanf 会尝试对 stdin 中已有数据以整型的格式进行解析。若解析成功,则将上述解析结果存放到指定的内存中,若解析失败,如 stdin 中仅存在一个字符 ‘a’,scanf 会退出并返回,但是上述不匹配的数据并不会从缓冲区中清除,后续的 scanf 调用仍从上述输入开始读取;

一些刁钻的例子

  • 例子1:scanf函数居然读取了所谓 空白字符——’\n‘
    1.4 输入缓冲区相关的笔记_第1张图片
    原因有几点:
    1. scanf不会读取’\n‘换行符,导致第一次scanf读取完数据后’\n‘残余在缓冲区内。
    2. 第二次因为要读取字符,而回车被视为字符,所以符合格式化输入要求,被读入。

解决方案:
1. 在两个scanf之间加入 fflush(stdin) 清除输入缓冲区
2. 或
char ch = getchar(); // 例如读取了一个字符
while ((c = getchar()) != EOF);
当读到末尾时getchar会返回EOF(-1)

3. **(新颖的方法)**用`scanf(" %c",&b);//加一个空格读取` 在 scanf 函数中,在格式说明符 %d 前面加一个空格的作用是==跳过任何前导空白字符(包括空格、制表符和换行符)==。这意味着 scanf 在读取整数之前,会忽略掉输入中的这些空白字符。
  • 例子2 Scanf函数输入字符串时 空格读取不到
    1.4 输入缓冲区相关的笔记_第2张图片
    scanf 通常不适用于读取包含空格的字符串,因为默认情况下它会将空格视为分隔符。 推荐使用gets。

总的来说

scanf既然叫格式化输入

  1. 只读取对应格式的数据,其他数据撇开不看。(正解释了第一点划线部分)
  2. 在缓冲区读取完对应的数据就退出函数,不用等读取到回车。(注意区分:输入数据到缓冲区时还是要回车)

getchar函数

实际上getchar函数就是scanf的缩水版,直接看作scanf("%d",&x);即可。
getchar一次只读一个字符,也不会清除输入时带的’\n‘,所以在使用多次输入时要注意清空缓冲区

等待用户输入的字符输入 getch与getche

getchar 可以直接从缓冲区中读取字符,而不等待用户输入,但这种方式也有可能带来潜在的错误。这里给出两种等待用户输入的字符传入方式。

使用 getche 与 getch 函数。上述函数均从键盘上读入一个字节,其中后者不会将字符回显到屏幕上。以这两个函数读取字符时,都是通过调用函数读取一个键盘输入且只有一个。如调用 getche,键盘敲击 ‘abc’ 时,只有一个字符 ‘a’ 会被读取。其他字符为无效输入。但上述函数并不被包含在标准 C 函数库中,需要通过头文件 conio.h 来使用,并不被所有的编译器实现支持。

printf

感谢双笙子佯缪老师
【C/C++】printf后并不会立即显示?文件流的缓冲机制探究_哔哩哔哩_bilibili

你可能感兴趣的:(C语言进阶,笔记,c语言)