为什么C++标准处理文件寻址的方式是这样的?

17

C++使用streamoff类型来表示(文件)流中的偏移量,定义如下 [stream.types]:

using streamoff = implementation-defined ;

类型streamoff是有符号基本整数类型之一的同义词,大小足以表示操作系统的最大可能文件大小。287)

287) 通常为long long。

这样做有意义,因为它允许在大型文件内进行寻址(而不是使用可能仅32位宽度的long)。

[filebuf.virtuals] 将basic_filebuf 的定位函数定义为以下内容:

pos_type seekoff(off_type off, ios_base::seekdir way, ios_base::openmode which = ios_base::in | ios_base::out) override;

off_type 等同于 streamoff,请参阅 [iostreams.limits.pos]。但标准随后解释了函数的效果。我对最后一句话感到烦恼,需要调用fseek

效果: 让width表示a_codecvt.encoding()。如果is_open() == false 或者 off != 0 && width <= 0,则定位操作失败。否则,如果 way != basic_ios::cur 或者 off != 0,并且(which & ios_base::out) == ios_base::out,则调用fseek来更改位置。

如果上一个操作是输出,则更新输出序列并写入任何未移动的序列。 接下来,寻找新的位置:如果width>0,则调用fseek(file,width * off,whence),否则调用fseek(file,0,whence)fseek接受一个long参数。如果off_typestreamoff被定义为long long(如标准建议的那样),则在调用fseek(file,width * off,whence)时可能会发生向下转换为long的情况(导致潜在难以诊断的错误)。这就对首次引入streamoff类型的整个基本原理提出了质疑。
这是有意还是标准中的缺陷?

8
缺陷看起来像什么。 - Yakk - Adam Nevraumont
我认为我注意到gcc libstdc++使用[fseeko64](https://github.com/avsm/src/blob/master/gnu/gcc/libstdc%2B%2B-v3/include/ext/stdio_sync_filebuf.h#L171)。 - KamilCuk
2
一时之间,看起来 seekoff 并不一定在底层使用 fseek。相反,fseek 的(可能是熟悉的?)行为被用来解释 seekoff 在做什么。 - jjramsey
@jjramsey 这也是我的印象。然而,它的措辞似乎表明这是一个要求,而不是一个解释。 - jceed2
这是一个描述“效果”的段落。 - Peter
1
@jjramsey 我同意,“Effects”部分可以合理地解释为只要它做了与fseek相同的效果,就不必实际调用fseek。但是,如果使用小于LONG_MIN或大于LONG_MAX的偏移量调用fseek将没有任何效果,因此对于streamofflong更宽的实现来说,该解释至少是不完整的。 - Keith Thompson
1个回答

6
我认为你从中得出的结论,即C++流和fseek之间存在不匹配,会导致运行时错误,是不正确的。情况似乎是这样的:
  1. long为64位的系统上,streamoff被定义为long,而seekoff函数调用fseek

  2. long为32位但操作系统支持64位文件偏移量的系统上,streamoff被定义为long long,而seekoff调用一个名为fseekofseeko64的函数,该函数接受64位偏移量。

这是我Linux系统上seekoff的定义片段:
#ifdef _GLIBCXX_USE_LFS
    if (!fseeko64(_M_file, __off, __whence))
      __ret = std::streampos(ftello64(_M_file));
#else
    if (!fseek(_M_file, __off, __whence))
      __ret = std::streampos(std::ftell(_M_file));
#endif

LFS代表大文件支持

结论:虽然标准建议streamoff的定义明显与要求seekoff调用fseek冲突,但库设计人员理解他们必须调用接受操作系统支持的完整偏移量范围的fseek变体。


6
情况似乎是:实现不允许在 seekoff 中不调用 fseek。它必须调用 fseek,但它没有这样做,标准规定它必须调用。我可以辩称这个实现是无效的。我认为它没有回答问题。哦,发现llvm,它调用了 fseeko - KamilCuk
提供一点信息,VC++ 在此函数中调用了 _fseeki64,这似乎也违反了标准规范。 - ChrisMM
1
这是实现者意识到问题并忽略标准的一个案例。我很高兴他们这样做了,但标准真的需要被修复。 - NathanOliver
1
有些人太过于字面理解标准了。它并不要求实现必须字面上调用名为 fseek 的函数。在其他地方,标准将某些内容描述为“仿佛通过调用 fseek(...) 实现”。如果它真的那么在意字面上调用 fseek,那么这个陈述就会不同。说真的,如果你正在实现一个 C++ 库,你会怎么做?你会坚持使用 64 位文件偏移量的低 32 位来调用 fseek 吗,因为文档告诉你要这样做吗?你的客户会感激你吗? - Willis Blackburn
如果我们字面上理解标准,那么意味着在一个根本没有名为“fseek”的API的系统上实现C++ I/O库是不可能的。我不认为这是意图所在。 - Willis Blackburn
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接