2022年9月16日:大変お騒がせしましたが、本件は本日解決し、↓に詳しく述べております。なお、下にあるコードは修正箇所を青太字で明記しておきますので、ご確認ください。
【オリジナルブログ】
現在新しいCSTRのFind、Replace、ReplaceAll関数を使ったツール、BCC2ECCを開発しています。
そんな中で、
怪談「bcc32c(bcc102)でコンパイルすると意図通り正常動作するのに、bcc32(bcc55)でコンパイルすると突然落ちる」
という怪奇現象に遭遇しました。
「何を大げさな。あんたのバグでしょ?」とおっしゃるあなた、以下をよく読んで何が原因か教えてください。
1.現象
(1)CSTRの新しいReplaceAll関数で「, "」という文字列(ToFind 文字列、", \"")を「, L"」という文字列(ToReplace 文字列、", L\"")に置き換える処理を行っている際、
(2)最初の「, "(MS 明朝")」、次の「, "(STATIC")」は意図通りの動作をするが、三番目の「, "(EDIT")」で、','の位置に0(NULL)を代入できない。
(3)その結果文字列のメモリー領域を超えて文字列が付加され落ちる。
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) {
DWORD dspl = Found_at - m_str; //Found_atのm_str内の相対位置
//置換後の文字列データバッファー(NULL終端付)の確保
char* buff = new char[OrgLen - ToFindLen + ToReplaceLen + 1];
MessageBox(0, Found_at, "Test - Found_at", MB_OK); //①
*Found_at = NULL; //NULL終端
lstrcpy(buff, m_str); //検索文字列(ToFind)までをbuffにコピー
MessageBox(0, m_str, "Test - m_str", MB_OK); //②
lstrcat(buff, ToReplace); //検索文字列(ToFind)の代わりに置換文字列をコピー
MessageBox(0, buff, "Test - buff", MB_OK); //③
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); //置換後のデータをコピー
Found_at = m_str + dspl; //Found_atのm_str内の相対位置
delete [] buff; //文字列データバッファーを破棄
}
return Found_at; //置換できなかった場合NULL、できた場合置換文字列の先頭ポインター
}
3.正常動作(, "STATIC")の場合のメッセージ
①
ToFindである「, "」(STATIC"...)を探しました。
②
「,」をNULLにして、それまでのm_str文字列をbuffに入れます。
③
ToFindの前のm_strをもらったbuffは末尾にToReplaceを追加します。
4.異常動作(, "EDIT")の時のメッセージ
①
ToFindである「, "」(EDIT"...)を探しました。ここまでは同じです。
②
「,」をNULLにした筈なのに、m_strは末尾までになっています。
③
その結果、buffにはm_str全文が入り、末尾にToReplace「, L”」を付加せざるを得なくなっています。
4.結語
正直私には何故「*Found_at = NULL; 」の文が正常に働かないのか、また何故第3番目の文字列で生じるのか、また何故bcc32(bcc55)で生じ、bcc32c(bcc102)では生じないのか、全くわかりません。
「実に面白い」
というガリレオ先生、いませんか?
ps. 実はCSTRクラスに関してはまだ怪談があり、それはある非公開のソフトで「外部変数で宣言したCSTRインスタンスにconst char*文字列を代入すると落ちる」という現象です。それも(そしてそれは真逆で)「bcc32(bcc55)でコンパイルすると正常に動作するのですが、bcc32c(bcc102)でコンパイルすると落ちる」のです。これも原因は不明であり、現在bcc55版だけを使っています。bcc2eccについてもbcc102版だけリリースすることになりそうです。