freopen() 在缓冲(setvbuf())方面的预期行为?

Dev*_*lar 7 c standards standard-library language-lawyer c-standard-library

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

因此...freopen()将关闭流(忽略错误),清除其错误和 EOF 标志,重置宽方向,然后使用给定模式重新打开流。这已经很清楚了;这基本上是一个 fclose() / fopen()。即使它不是这样定义的,但很明显这就是我们的意图。

但是,我有两个关于setvbuf()可以对流执行的操作的问题 - 设置用户分配的缓冲区和/或更改缓冲区策略。


问题1.

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

规范fclose()指定用户通过的与流关联的任何缓冲区都setvbuf()被取消关联,即现在可以free()由用户设置。

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

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


问题2。

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

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


我的看法是。

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

缓冲政策的类似考虑。

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

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

And*_*nle 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);
Run Code Online (Sandbox Code Playgroud)

描述

2freopen函数打开文件名是由stream指向的字符串的文件filename,并将stream指向的流与其关联起来。参数mode的使用方式与函数中一样fopen272)

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

4freopen函数首先尝试关闭与指定流关联的任何文件。忽略关闭文件失败的情况。流的错误和文件结束指示器被清除。

退货

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


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

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

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

对我来说,结论似乎是不可避免的: freopen()不会创建新的流。它只是将预先存在的流指向一个新文件 - 这就是它所做的全部。

当前实现支持这种读取——当前流的缓冲区状态未被修改。它们不会修改预先存在的流的缓冲状态。

GLIBC实现OpenSolaris freopen()/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
Run Code Online (Sandbox Code Playgroud)

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