在执行 scanf 之前 printf 如何被刷新?

如何解决在执行 scanf 之前 printf 如何被刷新?

我最近阅读了很多关于标准输出缓冲的内容。我知道 printf 是缓冲的,但到目前为止我认为它的缓冲区只有在新行被读入缓冲区或 fflush(stdout) 被调用或调用 printf 的进程时才会被刷新正常退出。

我编写了这个程序,它在 scanf 之前没有换行就调用 printf。当我用谷歌搜索时,我发现很多人说他们不明白为什么在 printf 之前执行 scanf。由于我现在了解标准输出缓冲的概念,这对我来说很有意义。

然而,就我而言,缓冲区在我运行 scanf 之前被刷新。这样做确实有意义,因为用户可能希望在任何 scanf 之前执行 printf,但它是如何发生的?究竟什么是刷新标准输出?是scanf吗?

int main(void) {
    char things;
    printf("Hello ");
    scanf("%c",&things);
}

(我正在运行 Arch Linux)

编辑:由于一些评论说我的系统的标准输出是无缓冲的,我只想补充一点,而无需在我的程序上运行 scanf,我的程序完全具有我上面提到的行为,它绝对是缓冲的。

>

解决方法

这是一个实施质量问题。

C 标准仅要求 stdinstdout 在默认情况下仅在附加到常规文件时才被完全缓冲。但它明确鼓励交互式设备的特定行为:

5.1.2.3 程序执行
[...]
对一致实现的最低要求是:
[...]
交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。

在许多 Posix 系统上,stdinstdout 在连接到字符设备时是行缓冲的,然后当 stdout 的读取尝试需要读取时自动刷新 stdin来自底层系统句柄。这允许即使没有尾随换行符也能在终端上显示提示。

在 linux 上,此行为在 stdio(3) linux manual page 中指定:

引用终端设备的输出流总是行 默认缓冲;写入此类流的待处理输出 每当引用终端的输入流时自动 设备被读取。在大量计算的情况下 在输出终端上打印部分行后完成,它是 有必要在关闭之前 fflush(3) 标准输出和 计算以便出现输出。

然而 GNU libc 有一个微妙的不同行为:只有 stdout 以这种方式刷新,如 glibc/libio/fileops.c 中的编码(由 Ulrich Drepper 在 2001-08-04 修改23:59:30):

  /* Flush all line buffered files before reading. */
  /* FIXME This can/should be moved to genops ?? */
  if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
    {
#if 0
      INTUSE(_IO_flush_all_linebuffered) ();
#else
      /* We used to flush all line-buffered stream.  This really isn't
         required by any standard.  My recollection is that
         traditional Unix systems did this for stdout.  stderr better
         not be line buffered.  So we do just that here
         explicitly.  --drepper */
      _IO_acquire_lock (_IO_stdout);

      if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
          == (_IO_LINKED | _IO_LINE_BUF))
        _IO_OVERFLOW (_IO_stdout,EOF);

      _IO_release_lock (_IO_stdout);
#endif
    }
,

我认为只有在将新行读入缓冲区或调用 fflush(stdout) 或调用 printf 的进程正常退出时,才会刷新其缓冲区。

这不是 C 标准的意图。当流被行缓冲时,只要程序请求任何未缓冲的流或从“主机环境”(例如用户键入输入的终端窗口)获取输入的行缓冲流上的输入,也应该刷新输出,如 C 2018 7.21.3 3 所述:

…当流被行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。此外,当缓冲区被填满时,当在非缓冲流上请求输入时,或者当在需要传输字符的行缓冲流上请求输入时,字符将作为一个块传输到主机环境。宿主环境t ...

这只是表达了一个意图,标准进一步说对这些特性的支持是实现定义的,所以这在技术上是一个实现质量的问题。但是,就诊断消息的质量而言,这不是质量问题。关于支持是实现定义的以及关于标准输出是否行缓冲的保留在很大程度上是对各种旧计算机系统中的可行性或可能性的让步。在大多数现代 C 实现中,C 实现不应使用 C 标准的此许可作为不实现这些功能的借口。

以下是从无关流读取输入如何刷新标准输出的示例。当我使用 Xcode 11.3.1 在 macOS 10.14.6 上执行此程序时,读取与 /dev/null 无关的流时会刷新标准输出中的“Hello”,但仅在使用 {{1} 写入输出时不会刷新没有阅读:

printf

如果我们删除 #include <stdio.h> #include <unistd.h> int main(void) { printf("Hello"); FILE *dummy = fopen("/dev/null","r"); setvbuf(dummy,NULL,_IONBF,0); // Make dummy unbuffered. fgetc(dummy); // "Hello" appears on terminal. printf(" world."); // " world." does not appear on terminal. sleep(5); printf("\n"); // " world." appears on terminal. } setvbuf,“Hello”不会立即出现在终端上,这表明它是对无缓冲流的读取,导致标准输出被刷新.

我知道 fgetc 已缓冲...

这取决于具体情况。其实就是流有没有缓冲,C 2018 7.21.3 7 说:

…标准输入和标准输出流被完全缓冲当且仅当可以确定该流不引用交互式设备。

因此,如果你将程序的标准输出重定向到一个文件,它并不指向一个交互式设备,所以如果程序可以检测到它,它必须被完全缓冲。当程序的输出进入交互式终端窗口时,它不能被完全缓冲。 (替代方案是行缓冲和非缓冲,典型的 C 实现使用行缓冲。)'

因此,如果标准输入和标准输出都连接到交互式设备(并且无法检测到其他设备),那么 printf 输出应该在执行 printf 之前出现,因为标准输出是无缓冲的(所以 scanf 输出立即出现)或者因为标准输出是行缓冲的,并且在调用 printf 时刷新。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res