2015-06-29 27 views
2

以下代码读取包含某个值的文件,该值代表更多后续数据的长度。std :: iostream使用计数零和无效缓冲区读取或写入

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()返回空指针。

This question表明0的countstd::basic_istream::read有效,但对问题的第三个评论似乎指出了我的问题。

是否有效C++将大小为0的无效指针传递给std::basic_istream::read(或std::basic_ostream::write)?对我来说这似乎合乎逻辑,因为呼叫不应触碰缓冲区。

显而易见的解决方案是用if子句处理这个特殊情况,但我想知道MSVC是否再次出错。

这里是正在运行的程序罚款铿锵的编译例子:http://coliru.stacked-crooked.com/a/c036ec31abd80f22

+1

为了澄清,“这会导致与消息中止......” - 是由一个未捕获的异常* *触发?或者它是VS标准C++库的调试二进制文件中的runtime * assertion *。这似乎没有什么大的差别,但它是相关的。并且在*发布*版本中后面的行为是否相同? – WhozCraig

+0

@WhozCraig它说'Debug Assertion Failed!'并且在发布版本中不会发生。 – typ1232

+0

@ typ1232那么,为什么不在调试模式下只是做'data.resize(dataLen + 1)'? – Barry

回答

1

这里是什么标准说,在27.7.2.3 [istream.unformatted]第30和31约std::basic_istream<...>::read()(重点是我的):

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

影响:表现为未格式化的输入功能(如在27.7.2.3所描述的,第1段)。在构造一个sentry对象后,如果!good()调用setstate(failbit)可能会引发异常并返回。否则提取字符并将它们存储到阵列的连续位置,其第一个元素由s指定。字符被提取并存储,直到发生以下任一情况:

  • n字符被存储;
  • 文件结束发生在输入序列上(在这种情况下,函数调用setstate(failbit | eofbit),这可能会导致ios_base::failure)。

返回*this

当一个函数被描述为以阵列作为参数,有什么可以根据17.6.4.9 [res.on.arguments]段1被传递一些约束(消隐文本适用于其它实体):

以下各项适用于C++标准库中定义的函数的所有参数,除非另有明确说明。

  • 如果函数的参数具有无效值(例如函数域外的值或指针对其预期用途无效),则行为是未定义的。
  • 如果一个函数的参数被描述为一个数组,实际上传递给函数的指针应有一个值,使得所有的地址计算和访问对象(如果指针确实指向的第一个元素,这将是有效这样的阵列)实际上是有效的。
  • ...根据8.3.4 [dcl.array]段落1(注意

实际阵列不能为空的是,当恒定表达为不存在未指定的收益率大小的阵列,其仍是如此得到一个非零的大小最终):

...如果常数表达式存在时,它应是一个类型为std的转换常数表达式::的size_t和其值应大于零 。 ...

由于空指针不能指向非空数组期望传递数组的函数期望得到非空指针。换句话说,我认为你观察到的断言完全是按顺序的,根据标准给一个具有未定义行为的用户定义行为:一个空指针,即使传递给read()的零大小也会根据标准产生未定义的行为。

+0

所以基本上这个标准允许一个实现去引用指针,即使我没有看到当count等于零时合法使用它。很好的答案,谢谢! – typ1232