なぜヒープの解放でエラー?

解決


boze  2008-05-09 21:08:53  No: 68292  IP: 192.*.*.*

ファイルサイズでバッファーを確保し
そこに読み込むだけのプログラムなのですが
関数を抜けるとき(例ではwmain)に
デバッグエラーになります。

buf( len + 1 )だとエラーにならないのですが
+1する必要がないように思えて不思議です。

どなたかわかる方がいれば説明してもらえないでしょうか。

環境:
・Windows XP SP2
・VC++ 2005 SP1


エラー内容:
-------------------------------------------------
Debug Error!

Program: e:\study\c++\heaptest\debug\HeapTest.exe

HEAP CORRUPTION DETECTED: after Normal block (#74) at 0x004D0068.
CRT detected that the application wrote to memory after end of heap buffer.


(Press Retry to debug the application)
-------------------------------------------------


サンプルコード:
-------------------------------------------------
#include <fstream>
#include <vector>

int wmain( int argc, wchar_t* argv[] )
{
    if( argc < 2 )
        return 1;

    std::wifstream ifs( argv[ 1 ], std::ios::binary );
    if( !ifs )
    {
        return 1;
    }

    ifs.seekg( 0, std::ios::end );
    size_t len = ifs.tellg();
    std::vector<unsigned char> buf( len );
    ifs.seekg( 0, std::ios::beg );

    for( size_t i = 0; i < buf.size(); i++ )
    {
        ifs.read( reinterpret_cast<wchar_t*>( &buf[ i ] ), 1 );
    }

    ifs.close();
    return 0;
}
-------------------------------------------------

編集 削除
tetrapod  2008-05-09 21:54:46  No: 68293  IP: 192.*.*.*

あたりまえなような気が駿河
たとえば len=100 であったとして i=99 を処理する際には &buf[99] が read() に渡されることになる
wifstream.read は wchar_t に相当するサイズ分を書き込むわけで、
その結果 &buf[99] だけでなくその後ろも破壊される

wifstream を使う以上は vector<wchar_t> としないとバグっちゃうぞ
wifstream で sjis なファイルを読むのは意味がないような気がするけどさ

編集 削除
boze  2008-05-09 23:54:01  No: 68294  IP: 192.*.*.*

なるほど。
1バイトだけ読んで書き込んでくれるわけじゃないんですね。
ちょっと勘違いしていました。
ありがとうございます。

でも、書き込みの時じゃなくて、関数を抜けるときにエラーになるのは何でなのでしょうか?

あと、unsigned shortの変数に2バイトreadした場合(例えばbitmapの先頭'B''M')、2バイト読み込めない('B'しか読めてない)のですが、これもなぜかご存知でしたら教えていただけないでしょうか。

編集 削除
tetrapod  2008-05-10 07:57:21  No: 68295  IP: 192.*.*.*

> 書き込みの時じゃなくて、関数を抜けるときにエラーになるのは何でなのでしょうか?
そもそも C/C++ は1バイト書き込みごとにエラーチェックしたりする親切言語ではない
だから書き込みそのものでは一切エラーチェックされない
関数を抜けるときに vector::~vector が呼ばれるわけだが、
Debug 版はこれがメモリ破壊チェックしているのでエラーが出る
# Release 版はチェックしないので何も言わずに吹っ飛ぶ可能性が大

> 1バイトだけ読んで書き込んでくれるわけじゃないんですね。
> 2バイトreadした場合
「1バイト」という文言をどういう意味に使っているのかな?
wifstream にどのような挙動を期待しているのかな?
wifstream は実質「使えない」関数なので注意しないとだめだぞ

wifstream は以下のような挙動を行うので注意が必要だ
ファイルから 'B' を読む→wchar_t に変換する→wchar_t の L'B' を wchar_t バッファにストアする
ファイルから 'M' を読む→wchar_t に変換する→wchar_t の L'M' を wchar_t バッファにストアする

ファイルから SJIS の第一バイトを読む→wchar_t に変換(失敗)する→
第一バイトのコード(=漢字になっていない)を wchar_t バッファにストアする
ファイルから SJIS の第二バイトを読む→wchar_t に変換(失敗)する→
第二バイトのコード(=漢字になっていない)を wchar_t バッファにストアする

意味がない、と先にコメントした理由はこういうことなわけだ

編集 削除
boze  2008-05-12 10:09:46  No: 68296  IP: 192.*.*.*

> wifstream は以下のような挙動を行うので注意が必要だ
> ファイルから 'B' を読む→wchar_t に変換する→wchar_t の L'B' を wchar_t バッファにストアする
> ファイルから 'M' を読む→wchar_t に変換する→wchar_t の L'M' を wchar_t バッファにストアする

'B'(0x42)と'M'(0x4d)を読んで、0x4d42をwchar_tバッファにストアする挙動を期待していました。
read(_Elem *_Str, streamsize _Count)は、_Countバイトまでsizeof(_Elem)バイトずつ読み込む処理を繰り返すものと思い込んでいました。

streamsizeとバイト数あたりの認識があやふやだったように思います。
なんとなく理解できた気がしますが、もう少し試してみます。
大変参考になりました。
ありがとうございました。

編集 削除