char** の new と delete について

[戻る]
佐藤 2006/06/16(金) 21:48:37
初めまして。
似たような項目はあったのでですが若干違うので質問させてください。

現在ポインタの勉強をしていて、可変の文字列のソートをやっています。
以下ソースです。

std::string           str_line;
std::vector< char* >  tmp;
unsigned int          g_size;
char**                g_container;

// ファイルから1行毎読み込む
while()
{
    /* 中略 */

    // 登録
    char* ch = new char[ str_line.size() ];
    strcpy( ch, str_line.c_str() );
    tmp.push_back( ch );
}

g_size      = static_cast< unsigned int >( tmp.size() );
g_container = new char* [ g_size ];

for( unsigned int i = 0; i < g_size; ++i )
    g_container[ i ] = tmp[ i ];
tmp.clear();

// ここにソート関数が入る。swapの所のみ記載
char* tmp            = g_container[j];
g_container[j]   = g_container[i];
g_container[i]   = tmp;

// 解放
for( unsigned int i = 0; i < g_size; ++i )
    delete[]  g_container[ i ];     // ここで落ちる
delete[]  g_container;



丁度 delete[] のところで落ちてしまいます。
配列の確保・解放の仕方はこれであっているのでしょうか?

以上です。宜しくお願いします。

【環境】WinXP VC.net2003

Blue 2006/06/16(金) 21:58:37
> char* ch = new char[ str_line.size() ];
'\0'を含むから、

char* ch = new char[ str_line.size() + 1 ];

では?

佐藤 2006/06/16(金) 22:11:31
レスありがとうございます。

while の中を省略してしまったのがいけなかったです。
一応'\0'はつけております。

pos は現在の position 。size はファイルサイズです。


while( pos < size )
{
    // 1行分をバッファに格納
    str_line.clear();
    while( ( pos < size ) && ( buf[pos] != '\r' ) && ( buf[pos] != '\n' ) )
    {
        char    c[2] = { buf[pos], '\0' };
        str_line.append( c );
        ++pos;
    }
    // CR-LFの処理
    if( ( pos < size ) && ( buf[pos] == '\r' ) )    ++pos;
    if( ( pos < size ) && ( buf[pos] == '\n' ) )    ++pos;

    // 登録
    char*    ch = new char[ str_line.size() ];
    strcpy( ch, str_line.c_str() );
    tmp.push_back( ch );
}

reshia 2006/06/16(金) 22:24:54
こういうときは、
 new  したときに得られた領域のアドレスと
 delete しようとしている領域のアドレスが
一致しているかを調べてみればいいと思います。

具体的には、次のようにprintfを入れます。

    // 登録
    char*    ch = new char[ str_line.size() ];
    printf("new : %p\n", ch);  // ←追加

    // 解放
    for( unsigned int i = 0; i < g_size; ++i )
    {
        printf("del : %p\n", g_container);  // ←追加
        delete[]  g_container[ i ];
    }

reshia 2006/06/16(金) 22:25:51
↑間違えた。

間違い:
    // 解放
    for( unsigned int i = 0; i < g_size; ++i )
    {
        printf("del : %p\n", g_container);  // ←追加
        delete[]  g_container[ i ];
    }


修正:
    // 解放
    for( unsigned int i = 0; i < g_size; ++i )
    {
        printf("del : %p\n", g_container[i]);  // ←追加
        delete[]  g_container[ i ];
    }

佐藤 2006/06/16(金) 22:30:50
あああ、Blue様勘違いしておりました。
確かに new char[ str_line.size() + 1 ] でした。

少し試してみます。

K 2006/06/16(金) 22:31:02
原因を絞るために、ソート部分をなくして
読み込み -> 解放で落ちないかチェックしてみては?

ポインタの勉強なら仕方ないですが、
パフォーマンスの問題でもない限り、
std::vector<std::string>
が確実だと思います。

ベタでnew, deleteは基本的に使いません。
バグ(の元)ですから。

佐藤 2006/06/16(金) 22:44:03
reshia様、K様レスありがとうございます。

ソートを切ってポインタの表示をさせてみたのですが
ポインタは同じようでした。(ちなみに赤×メッセージです↓)

DAMAGE : after Normal block (#45) at 0x00370FF0.


>std::vector<std::string>が確実だと思います。

ごもっともです。最初は vector を使用してやっていたのですが
データ数増やしてみたら恐ろしく遅かったので、勉強を兼ねてポインタにした次第であ
ります。

少し試してみます。

reshia 2006/06/16(金) 22:57:00
> DAMAGE : after Normal block (#45) at 0x00370FF0.
これって、確保した領域外にアクセスしてから解放したときにでる・・
こんな↓コードで再現します。

    int* arr = new int[10];
    for (int i = 0; i <= 10; i++) {
        arr[i] = i;
    }
    delete[] arr;
    return 0;

reshia 2006/06/16(金) 22:58:27
>ソートを切ってポインタの表示をさせてみたのですが
>ポインタは同じようでした。(ちなみに赤×メッセージです↓)

ソートが原因だったとしたら、
ソートを切った状態でポインタを・・いやアドレスを表示しても意味がないですよ。

佐藤 2006/06/16(金) 23:07:16
間違った所がありました。

Blue様のご指摘を受けて修正した箇所を
char*    ch = new char[ str_line.size() + 1 ];
strcpy( ch, str_line.c_str() );
// ch[ str_line.size() + 1 ] = '\0'; // ←こうしてました。
ch[ str_line.size() ] = '\0';

修正してみたら落ちなくなりました。

佐藤 2006/06/16(金) 23:10:59
[[解決]]
入れ違いでした。

>> DAMAGE : after Normal block (#45) at 0x00370FF0.
>これって、確保した領域外にアクセスしてから解放したときにでる・・

なるほど!

つまり一番最初のコードの時も'\0'分確保してないにもかかわらず
strcpyで範囲外にアクセスしてたって事なんですかね。

その場で出ないで delete の時にエラーになるんですね。
勉強になりました。


とりあえず落ちなくなったので解決とさせて頂きます。

Blue様、reshia様、K様ご教示ありがとうございました。

古いスレッドには追加発言できません。