printf scanf 缓冲区

输入输出缓冲机制

1. 概述

​ 缓冲区又称为缓存,它是内存空间的一部分。在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或者输出的数据,这部分预留的空间叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

2. 为什么要有缓冲区

  1. 减少IO设备的操作
  2. 提供计算机运行速度

举个例子:从硬盘读取数据时,一次性读取的所需的数据放置在缓冲区,计算机再从缓冲区选择所需要的数据进行处理,而完成本次读取工作的硬盘便可以执行下一次读取工作,能大大提高计算机的运行速度。

3. 缓冲区的类型

  • 全缓冲

    • 当填满缓冲区后,才会进行实际IO操作。比如对硬盘文件的读写。
    • 默认用于磁盘文件(如 fopen 打开的文件流)。
  • 行缓冲

    • 遇到换行符\n时,自自动刷新缓冲区。
    • 默认用于终端交互(如 stdout 连接到终端时)。
  • 不带缓冲

    • 数据直接读写,不经过缓冲区。
    • 默认用于错误输出(如 stderr),确保错误信息即时显示。

系统在启动的时候会默认打开,三个输入输出流(文件)

  • stdin:键盘文件
  • stdout:显示器文件
  • stderr:显示器文件

4. 引发缓冲区的刷新

  1. 输出缓冲区(stdout)是行缓冲,当遇到换行符的时候,系统会将缓冲区的数据冲刷到标准输出设备上。
  2. 缓冲区满(溢出)
  3. 手动刷新执行 fflush(stdout)缓冲区
  4. 程序结束,也会冲刷到屏幕终端
#include <stdio.h>
 // Linux sleep() 秒
#include <unistd.h> 
// window Sleep() 毫秒
// #include <windows.h>

int main()
{
        printf("hello world");
        sleep(2);


        printf("hello world");
        fflush(stdout);


        printf("hello,直接刷新");
        sleep(2);

        printf("hello,直接刷新\n");

        printf("BUFSIZ: %d\n",BUFSIZ);

        pid_t pid = fork();

        if (pid < 0)
        {
        }
        else if (pid == 0)
        {
                printf("hello");
                sleep(3);
                return 0;
        }
        else
        {
                while(1)
                {
                        printf("父线程,PID = %d,子线程PID = %d\n",getpid(),pid);
                        sleep(1);
                }

        }
        return 0;

输出结果

Linux系统:这个系统下缓冲区表现的明显,会更直观看到是由于换行符\n,溢出或手动刷新。

在这里插入图片描述

window系统:这个系统下缓冲区表现不明显,以上代码是无法看出区别。

在这里插入图片描述

C 标准库提供了 BUFSIZ 宏(定义在 <stdio.h>),它表示默认缓冲区的大小(通常是 512 或 4096 字节,取决于系统)。

5. printf

概念

头文件

#include <stdio.h>

函数作用

​ 将指定格式的数据输出到屏幕终端上(输出设备)

函数原型

int printf ( const char * format, ... );
  • printf 第一个参数必须是字符串

  • printf 第二个参数是可变参数
    • 可变参数是接受 与格式控制符匹配的任意数量和类型的参数
    • 格式控制符:
格式符类型输出形式示例说明
%d有符号十进制整数-42普通整数输出
%u无符号十进制整数42无符号整数(如 unsigned int
%f十进制浮点数3.140000默认保留 6 位小数
%e科学计数法(小写)1.234560e+02指数形式(尾数 + e±xx),默认 6 位小数
%E科学计数法(大写)1.234560E+02%e,但指数符号 E 大写
%g自动选择 %e/%f3.141.23e+05根据数值大小自动选择更紧凑的格式(短格式优先)
%c单个字符A输出 ASCII 字符
%s字符串Hello输出字符串(以 \0 结尾)
%p指针地址0x7ffd3456以十六进制输出指针地址
%x十六进制(小写)ff无符号整数的十六进制(字母小写)
%X十六进制(大写)FF无符号整数的十六进制(字母大写)
%%百分号%输出 % 本身

5.1 字段宽度

用户没有明确指定左对齐,则默认采用右对齐输出

5.1.1 静态指定宽度
场景示例代码输出结果(假设值为 42"AB"
固定宽度(右对齐)printf("%5d", 42);∙∙∙42 代表空格,下同)
固定宽度(左对齐)printf("%-5d", 42);42∙∙∙
字符串宽度控制printf("%5s", "AB");∙∙∙AB
补零代替空格printf("%05d", 42);00042
字符串宽度精确控制`printf("%.3s

%Nd定义的宽度,是最小宽度。所以如果实际数据宽度超过预定的值,会按照实际宽度输出,不会发生截断。

5.1.2 动态指定宽度

可以用 * 占位符,通过参数动态传入宽度值:

int width = 8;
printf("%*d", width, 42);  // 等价于 "%8d"

6. scanf

概念

头文件

#include <stdio.h>

函数作用

​ 从屏幕终端上获取指定格式的数据,存储到变量的内存空间上

函数原型

int scanf ( const char * format, ... );
  • scanf 第一个参数必须是字符串

  • scanf 第二个参数是可变参数
    • 可变参数是接受 与格式控制符匹配的任意数量和类型的参数

函数返回值

  • 成功
    • 返回成功匹配并赋值的参数个数
  • 失败
    • 返回EOF,通常是-1。

在这里插入图片描述

格式字符串

(1) 格式字符串中显式添加空格

scanf("%d %d", &a, &b);  // 空格分隔两个 %d

读取第一个整数后,跳过任意数量的空白字符(包括换行),再读取第二个整数。

(2) 无空格(紧邻占位符)

scanf("%d%d", &a, &b);  // 无空格分隔

"%d %d" 效果相同!因为 %d 本身会跳过前导空白字符。

(3) 其他占位符(如 %c)的特殊情况

char c;
scanf("%c", &c);  // %c 不会跳过空白字符!

%c 会读取 任意字符(包括空格、换行符),特别是出现在多字符读取的时候,可能会出现意想不到的效果。

如下:

char a,b;
scanf("%c", &a);
printf("%c",a);
scanf("%c", &b);
printf("%c",b);
//输入 1回车2

在这里插入图片描述

会发现我2都没来得及输入,程序就结束了。

其实当输入1 、【回车】时,它两都被写入缓冲区中,而1用于a,【回车】就放在缓冲区,等下一次读取时,直接就读取缓冲区剩下的【回车】,程序就直接结束了。

所以在面对多字符读取时,应当重视缓冲区,避免发生误读。

解决方案

  1. 可用上述格式字符串中显式添加空格,可跳过任意数量的空白字符(包括换行)。
  2. 手动刷新缓冲区
while (getchar() != '\n'); // 清空缓冲区直到换行符

注:以上均为学习笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值