7

为了实现 freopen(),我在标准中提出了一条规范,据我所知,该规范实际上并没有指定任何内容。

所以...freopen()将关闭流(忽略错误),清除其错误和 EOF 标志,重置宽方向,然后以给定模式重新打开流。这很清楚;这基本上是一个 fclose() / fopen()。即使没有这样定义,很明显这就是预期的。

setvbuf()但是,关于可能对流所做的事情,我有两个问题- 设置用户分配的缓冲区和/或更改缓冲区策略。


问题 1。

1)freopen()预计将事物恢复到默认状态,就好像它实际调用过一样fopen()?或者,无论用户在旧流上设置了什么,它是否有望延续到新流setvbuf()?这指的是缓冲存储器和缓冲策略,但这里的主要问题是缓冲存储器。

的规范fclose()指定用户与流相关联的任何缓冲区setvbuf()都是解除关联的,即现在​​可以free()由用户'd'。

freopen()只指定它关闭与流关​​联的文件,而不是fclose()它。

那么,在 之后freopen(),用户关联的缓冲内存是否仍然与流关联?


问题2。

freopen()可以想象,可以FILE在调用时实际上与打开文件无关的结构上使用(因为尝试关闭文件的错误将被忽略)。

该文件结构可能是以前打开的流,具有用户分配的缓冲区内存和缓冲区策略。是freopen()遵守这些设置,即将缓冲存储器/策略与“重新”打开的文件重新关联,还是将结构重新初始化为默认值,假设用户在之前 ing 文件free()后使用缓冲存储器?fclose()


我的看法。

看看 Q2,我看不到标准库有一种方法可以可靠地确定FILE具有用户分配的缓冲内存的当前未打开的结构是否仍然“拥有”该缓冲内存,或者用户是否已经回收了该内存。(可以想象,该内存可能是本地的,即不是由malloc()/处理的内存列表的一部分,free()即使我愿意去那里——这将是标准库函数所期望的非常反常的工作。)

缓冲政策的类似考虑。

因此,据我所知,唯一可靠freopen()的处理方式是将“与指定流关联的任何文件”的关闭处理为“真实” fclose(),并将缓冲内存/策略重新设置为默认值。

我的理解是否正确,还是 Q1 / Q2 有其他答案?

4

1 回答 1

2

C 标准没有说明以任何方式修改缓冲状态。

整个C11freopen()规范是(包括脚注 272):

7.21.5.4freopen功能

概要

1

     #include <stdio.h>
     FILE *freopen(const char * restrict filename,
          const char * restrict mode,
          FILE * restrict stream);

描述

2freopen函数打开文件名是指向的字符串,并将filenamestream指向的流与其关联。mode参数的使用与函数fopen中一样。272)

3如果filename是空指针,则freopen函数尝试将流的模式更改为由 指定的模式mode,就好像当前与流关联的文件的名称已被使用一样。允许哪些模式更改(如果有)以及在什么情况下是实现定义的。

4freopen函数首先尝试关闭与指定流关联的任何文件。未能关闭文件将被忽略。流的错误和文件结束指示符被清除。

退货

5freopen如果打开操作失败,该否则,freopen返回 的值stream


272)freopen函数的主要用途是更改与标准文本流( 、 或 )关联的文件stderrstdin因为stdout这些标识符不需要是可修改的左值,fopen函数返回的值可以分配给这些左值。

对我来说,关键短语是将流指向的流与它相关联。指向stream的预先存在的流有一个与之关联的新文件——仅此而已。通过不指定对缓冲的任何更改,这对我来说意味着保留当前的缓冲区状态,就像freopen()将新文件和模式与预先存在的流相关联一样。根据我的阅读,只有那些在标准中明确指出的流的更改才应该进行。FILE *

还要注意第 4 段: freopen函数首先尝试关闭与指定流关联的任何文件。 同样,标准指的是指定的流

对我来说,结论似乎是不可避免的: freopen()不会创建新的流。它只是将预先存在的流指向一个新文件——仅此而已。

当前实现支持此读数 - 当前流的缓冲区状态未修改。它们不会修改预先存在的流的缓冲状态。

GLIBC实现freopen()OpenSolaris/Illumos 实现很可能是当前的 Solaris 实现)似乎都没有修改原始缓冲的状态,而不是在关闭文件之前刷新任何缓冲区。

freopen()功能似乎没有明确指定。 POSIX 有这样的说法

应用程序使用

该函数通常用于将与、和freopen()关联的预先打开的流附加到其他文件。stdinstdoutstderr

pathname由于当参数为时,实现不需要支持任何流模式更改,因此NULL可移植应用程序不能依赖于freopen()更改流模式,因此不鼓励使用此功能。该功能最初被添加到 ISO C 标准中,以便于更改stdinstdout二进制模式。由于'b'模式中的字符对 POSIX 系统没有影响,因此在 POSIX 应用程序中不需要使用该功能。然而,即使'b'被忽略,成功调用freopen (NULL, "wb", stdout)确实有效果。特别是,对于常规文件,它会截断文件并将流的文件位置指示符设置为文件的开头。这些副作用可能是 ISO/IEC 9899:1999 标准中指定该功能的方式的意外结果,但除非或直到 ISO C 标准发生更改,否则成功调用的应用程序freopen (NULL, "wb", stdout)将在符合以下情况的系统:

{ appl file1; appl file2; } > file3

这将导致file3仅包含第二次调用appl的输出。

于 2020-02-07T14:34:36.050 回答