実はBCC2ECCをECCSkeltonに移植し、また↓の不具合

が発生しました。如何にも文字列処理の暴走っぽいのですが、訳が分からないまま、今朝早朝、

「あ"っ」

「またやっちまった」

ということで、デバッグ直観が降りてきました。

 

実は書く(告白する)のも恥ずかしくて死にそうなんですが、「元カノ(カレ)を捨てたのに、実はずーーーーーっと使っていた」のがバグの原因でした。(以下↑の馬鹿ブログからの引用)

元々の文字列(文字配列)m_strと、m_strの中でToFind文字列を発見したFound_atに注意して以下を見てください。

 

2.該当箇所のコード(デバッグ用のメッセージボックス付)

//文字列の置換
char* CSTR::Replace(char* ToFind, char* ToReplace, char* sp = 0) {

    
//CSTRのデータ長、検索文字長、置換文字長を計算する
    int OrgLen = lstrlen(m_str);
    int ToFindLen = lstrlen(ToFind);
    int ToReplaceLen = lstrlen(ToReplace);
  
 //spの指定が無ければCSTRデータの先頭から検索する
    if(!sp)
        sp =
m_str;
  
 //検索文字列ポインターの取得
    char* Found_at = strstr(sp, ToFind);        //検索文字列が無ければNULLを返す
    //検索文字があれば
    if(Found_at) {
(A)

        //置換後の文字列データバッファー(NULL終端付)の確保
        char* buff = new char[OrgLen - ToFindLen + ToReplaceLen + 1];
        *
Found_at = NULL;                        //NULL終端
        lstrcpy(buff, m_str);                    //検索文字列(ToFind)までをbuffにコピー
        lstrcat(buff, ToReplace);                //検索文字列(ToFind)の代わりに置換文字列をコピー
        lstrcat(buff, Found_at + ToFindLen);    //検索文字列(ToFind)以降の文字列をコピー
        buff[OrgLen - ToFindLen + ToReplaceLen] = NULL;    /付/NULL終端をける
        delete [] m_str;                        //CSTRデータ(m_str)を削除
        m_str = new char[OrgLen - ToFindLen + ToReplaceLen + 1];    //新しいデータ領域を確保
        lstrcpy(m_str, buff);                    //置換後のデータをコピー
(B)

        delete [] buff;                            //文字列データバッファーを破棄
    }
    return
Found_at;    //置換できなかった場合NULL、できた場合置換文字列の先頭ポインター
}

 

そうです。もともとの文字列m_strは一旦buffにコピーされ、捨てられ(delete)て、新しいm_strになります。しかし、元カノのFound_atをそのまま使っていた、というお粗末です。(一旦廃棄されたm_strのメモリー領域は再度m_strを作る際にかぶって使われると、結構問題なく動いちゃうこともあるので、本当のバグ原因が分からなくなり、bcc32を悪者にしてしまったようです。年を取ると「思い込むと自分が正しいと盲信し、その他のことを忘れてしまう」様子がよく分かると思います。)

 

ソリューションとしては、

(A)    DWORD dspl = Found_at - m_str;            //Found_atのm_str内の相対位置

(B)    Found_at = m_str + dspl;                //Found_atのm_str内の相対位置

というコードを挿入することで修正しています。

 

とんだお粗末、というか(自分でも愕然とする)ボケをかまして、いよいよリタイアかと、しょんぼりしてしまいした。

BCCSkelton、ECCSkelton、BCC2ECC全て本日修正アップロード済です。

 

追記:なお、前のブログで「何故こうなるのだろう?」という現象の説明をすると、次のようになります。

(1)ToFind文字列が見つからなければ文字列やメモリー操作はされないので問題はありません。

(2)最初のToFind文字列がm_strで見つかった場合、その先頭アドレスをNULLにしてbuffにコピーし、ToReplace文字列を追加して、最後に残ったm_str文字列をbuff追加するまでは問題ありません。が、buffをコピーするのは新しいm_strになります。にもかかわらず、Found_atはdeleteで破棄された旧m_strのメモリーの中です。

(3)次の検索は「破棄された旧m_strのメモリーの中」で行われ、ToFind文字列の先頭アドレスをNULLにするのは良いのですが(実際旧m_strのメモリーではそうしている)、buffにコピーする「m_str」は新しいm_strなので、第1回目で作成された新しいm_str文字列がすべてコピーされてしまうことになります。そして想定外に長い文字列がbuffにコピーされた後、お尻にToReplaceを追加することになるので、確保したメモリー領域を超えて書き込むことになって、落ちてしまいます。