std::iostream 读取或写入计数为零且缓冲区无效

std::iostream read or write with count zero and invalid buffer

以下代码读取一个文件,该文件包含一些表示更多后续数据长度的值。

auto file = std::ifstream(filename, std::ios::in | std::ios::binary);
// dataLen = Read some header field containing a length of following data.
std::vector<unsigned char> data;
data.resize(dataLen);
file.read((char*)data.data(), dataLen);

如果 dataLen = 0,MSVC 2013 编译器将失败。它导致中止并显示消息 Expression: invalid null pointer,因为 data.data() returns 一个空指针。

This question 表明 count 为 0 对 std::basic_istream::read 有效,但对该问题的第三条评论似乎指出了我的问题。

将大小为 0 的无效指针传递给 std::basic_istream::read(或 std::basic_ostream::write)是否有效?这对我来说似乎合乎逻辑,因为无论如何调用都不应该触及缓冲区。

明显的解决方案是用 if 子句处理这种特殊情况,但我想知道 MSVC 是否又错了。

这里是 clang 的编译示例 运行 程序很好:http://coliru.stacked-crooked.com/a/c036ec31abd80f22

这是标准在 27.7.2.3 [istream.unformatted] 第 30 和 31 段(重点是我的)中关于 std::basic_istream<...>::read() 的说法:

basic_istream<charT,traits>& read(char_type* s, streamsize n);

Effects: Behaves as an unformatted input function (as described in 27.7.2.3, paragraph 1). After constructing a sentry object, if !good() calls setstate(failbit) which may throw an exception, and return. Otherwise extracts characters and stores them into successive locations of an array whose first element is designated by s. Characters are extracted and stored until either of the following occurs:

  • n characters are stored;
  • end-of-file occurs on the input sequence (in which case the function calls setstate(failbit | eofbit), which may throw ios_base::failure).

Returns: *this.

当一个函数被描述为以数组作为参数时,根据 17.6.4.9 [res.on.arguments] 第 1 段(省略的文本适用于其他实体),对于可以传递的内容有一些限制:

Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

  • If an argument to a function has an invalid value (such as a value outside the domain of the function or a pointer invalid for its intended use), the behavior is undefined.
  • If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.
  • ...

根据 8.3.4 [dcl.array] 第 1 段,实际数组不能为空(请注意,常量表达式不存在的情况会产生一个未指定大小的数组,最终仍会获得非零大小):

... If the constant-expression is present, it shall be a converted constant expression of type std::size_t and its value shall be greater than zero. ...

由于空指针不能指向非空数组函数,因此需要传递数组,因此需要非空指针。换句话说,我认为您观察到的断言完全是有序的,根据标准将定义的行为赋予具有未定义行为的使用:根据标准,即使传递给 read() 的零大小的空指针也会产生未定义的行为标准。