【问题标题】:Understanding undefined behavior for a binary stream using fseek(file, 0, SEEK_END) with a file使用带有文件的 fseek(file, 0, SEEK_END) 了解二进制流的未定义行为
【发布时间】:2014-01-29 19:08:03
【问题描述】:

C 规范有一个有趣的脚注(#268 C11dr §7.21.3 9)

“将文件位置指示器设置为文件结尾,与fseek(file, 0, SEEK_END) 一样,对于二进制流(因为可能出现尾随空字符)或任何具有与状态相关的编码但不确定的流具有未定义的行为以初始移位状态结束。”

这是否适用于读取文件的二进制流?(从物理设备)

IMO,磁盘上的二进制文件只是字节的海洋。在我看来,二进制文件不能具有与状态相关的编码,因为它是 binary 文件。我对“面向二进制宽的流”的概念以及它是否适用于磁盘 I/O 的概念很模糊。

我看到在串行流上调用fseek(file, 0, SEEK_END) (如com 端口)或stdin 可能无法达到真正的目的,因为结束 尚未确定。因此将问题缩小到物理文件。


[edit] 答:对老年人的担忧(可能直到 1980 年代后期)。目前在 2014 年,Windows、POSIT 特定和非异国情调的其他人:不是问题。

@Shafik Yaghmour 在Using fseek and ftell to determine the size of a file has a vulnerability? 中提供了很好的参考。 @Jerry Coffin 讨论了 CP/M 作为二进制文件并不总是具有精确的长度。 (每个 wiki 128 字节的记录)。

感谢@Keith Thompson 的回答。

这共同解释了规范的“(因为可能出现尾随空字符)”注释。

【问题讨论】:

标签: c file-io binary fseek


【解决方案1】:

在您可能使用的任何系统上,二进制文件都将是 8 位字节序列,具有精确指定的大小。但并非所有系统都以这种方式存储文件,C 标准经过精心设计,允许移植到具有不寻常特征的系统。

例如,一个符合标准的 C 实现可能在将文件存储为 512 字节块序列的操作系统上运行,而没有指示最终块中有多少字节是重要的。在这样的系统上,当创建二进制文件时,操作系统可能会用零字节填充最终块的其余部分。当您从此类文件中读取时,填充字节可能会出现在输入中(即使它们从未显式写入文件),也可能会被忽略(即使创建文件的程序可能已显式写入它们) .

如果您正在从不可搜索的流中读取数据(例如键盘输入),那么fseek(file, 0, SEEK_END) 不仅会给您一个错误的结果,它还会通过返回一个非零结果来指示失败。 (在 POSIX 兼容的系统上,它返回 -1 并设置 errno;ISO C 不需要。)

在大多数系统上,二进制文件上的fseek(file, 0, SEEK_END) 将寻找文件的实际末尾(由写入文件的确切字节数确定的位置),或返回明确的失败指示。如果您仍然使用 POSIX 特定的功能,您可以放心地假设这种行为;您可能可以对 Windows 和许多其他系统做出相同的假设。如果您希望您的代码 100% 可移植到外来系统,则不应假设二进制文件不会被额外的零字节填充。

【讨论】:

  • 除了CP/M,您知道当前有哪些文件系统不是“由写入的确切字节数决定”吗?
  • @chux:不,但我不熟悉所有当前的文件系统。 (嵌入式系统可能会有一些东西。)
  • “将是......一个精确的指定大小,在您可能使用的任何系统上”,我已经使用过很多文件系统,甚至一个像 CBM DOS 这样的 CP/M 操作系统,我相信它也缺少字节特定的文件大小。怀疑我很快就会为那个平台编写 C 代码。我虽然奇怪的 C 规范是关于新的东西,而不是关于旧的东西。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
相关资源
最近更新 更多