2015-06-29 32 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