実は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を追加することになるので、確保したメモリー領域を超えて書き込むことになって、落ちてしまいます。
