[英]Expected behaviour of freopen() with regards to buffering (setvbuf())?
在尝试实现 freopen() 时,我在标准中找到了一段规范,据我所知,该规范实际上并未指定任何内容。
所以... freopen()
将关闭流(忽略错误),清除其错误和 EOF 标志,重置宽方向,然后以给定的模式重新打开流。 这很清楚; 这基本上是一个 fclose() / fopen()。 即使它不是那样定义的,很明显这就是它的意图。
但是,我有两个关于setvbuf()
可以对流执行的操作的问题——设置用户分配的缓冲区和/或更改缓冲区策略。
问题 1。
1) freopen()
希望将事物恢复到默认状态,就好像它实际上已经调用了fopen()
? 或者,无论用户通过setvbuf()
在旧的流上设置什么,它都有望转移到新的流中吗? 这涉及缓冲存储器和缓冲策略,但这里的主要问题是缓冲存储器。
fclose()
规范指定用户通过setvbuf()
与流关联的任何缓冲区都被取消关联,即现在可以由用户free()
。
但是freopen()
只指定它关闭与流关联的文件,而不是它fclose()
es它。
那么,在freopen()
,用户关联的缓冲内存是否仍然与流相关联?
问题2。
可以想象, freopen()
可以用于在调用时实际上与打开文件无关的FILE
结构(因为尝试关闭文件的错误将被忽略)。
该文件结构可能是先前打开的流,具有用户分配的缓冲内存和缓冲策略。 freopen()
为了遵守这些设置,即将缓冲内存/策略与“重新”打开的文件重新关联,还是将结构重新初始化为默认值,假设用户在fclose()
free()
之后使用free()
d 缓冲内存fclose()
之前的文件?
我的。
看看第二季度,我没有看到标准库可以可靠地确定具有用户分配的缓冲内存的当前未打开的FILE
结构是否仍然“拥有”该缓冲内存,或者用户是否已经回收了该内存. (可以想象,该内存可能是本地的,即不是malloc()
/ free()
处理的内存列表的一部分,即使我愿意去那里——这将非常不寻常地涉及标准库函数所期望的工作。)
缓冲政策的类似考虑。
因此,据我所知,处理事情的唯一可靠方法是freopen()
将“与指定流关联的任何文件”的关闭处理为“真正的” fclose()
,并重新设置缓冲内存/策略为默认值。
我的这种理解是对的,还是 Q1/Q2 有其他答案?
C 标准没有声明以任何方式修改缓冲状态。
整个C11 freopen()
规范是(包括脚注 272 ):
7.21.5.4
freopen
函数概要
1
#include <stdio.h> FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream);
描述
2
freopen
函数打开名称为filename
指向的字符串的filename
,并将stream 指向的流与其关联。mode
参数的使用就像在fopen
函数中一样。 272)3如果
filename
是空指针,则freopen
函数尝试将流的模式更改为 mode 指定的mode
,就好像使用了当前与流关联的文件的名称一样。 实现定义了哪些模式更改是允许的(如果有的话),以及在什么情况下。4
freopen
函数首先尝试关闭与指定流关联的任何文件。 关闭文件失败将被忽略。 流的错误和文件结束指示符被清除。退货
5如果打开操作失败,
freopen
函数返回一个空指针。 否则,freopen
返回stream
的值。
272)
freopen
函数的主要用途是更改与标准文本流(stderr
、stdin
或stdout
)相关联的文件,因为这些标识符不需要是可修改的左值,由fopen
函数返回的值可以分配给这些左值。
对我来说,关键短语是将流指向的流与它相关联。 stream 指向的预先存在的stream
有一个与之关联的新文件——仅此而已。 通过不对缓冲指定任何更改,这对我来说意味着保留当前的缓冲区状态,因为freopen()
只是将新文件和模式与预先存在的流相关联。 通过我的阅读,只应该对标准中明确指出的FILE *
流进行那些更改。
还要注意第 4 段: freopen
函数首先尝试关闭与指定流关联的任何文件。 同样,标准是指指定的流。
对我来说,结论似乎是不可避免的: freopen()
不会创建新的流。 它只是将预先存在的流指向一个新文件——这就是它所做的一切。
当前的实现支持这种读取——当前流的缓冲区状态未被修改。 它们不会修改预先存在的流的缓冲状态。
GLIBC freopen()
实现和OpenSolaris/Illumos 实现(很可能是当前的 Solaris 实现)似乎都没有修改原始缓冲的状态,除了在关闭文件之前刷新任何缓冲区。
freopen()
函数似乎没有详细说明。 POSIX 有这样的说法:
应用使用
freopen()
函数通常用于将与stdin
、stdout
和stderr
关联的预打开流附加到其他文件。由于当
pathname
参数为NULL
,实现不需要支持任何流模式更改,因此可移植应用程序不能依赖使用freopen()
来更改流模式,因此不鼓励使用此功能。 该功能最初被添加到 ISO C 标准中,以便于将stdin
和stdout
更改为二进制模式。 由于模式中的'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的输出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.