2

私はBDS 2006 Turbo C++を長い間使用していますが、より大きなプロジェクト ( CAD/CAM、3D gfx エンジン、および天文計算) の一部で例外がスローされることがあります (たとえば、24 時間 365 日のヘビーデューティー使用で 3 ~ 12 か月に 1 回)。 )。広範なデバッグの後、私はこれを見つけました:

//code1:
struct _s { int i; }    // any struct
_s *s=new _s[1024];     // dynamic allocation
delete[] s;             // free up memory

このコードは通常、テンプレート内に_sあり、クラスにもなる可能性があるため、delete[]このコードは適切に機能するはずですが、delete[]構造体では適切に機能しません (クラスは問題ないように見えます)。例外はスローされず、メモリは解放されますが、何らかの形でメモリ マネージャーの割り当てテーブルが破損し、その後、新しい割り当てが間違っている可能性があります (新しい割り当ては、既に割り当てられている領域または割り当てられていない領域と重複する割り当てを作成する可能性があるため、時折例外が発生します)

_s空のデストラクタを追加すると、突然すべてが正常に見えることがわかりました

struct _s { int i; ~_s(){}; }

さて、奇妙な部分が来ます。AnsiStringこれをプロジェクトに更新した後、クラスにも不適切な再割り当てがあることがわかりました。例えば:

//code2:
int i;
_s *dat=new _s[1024];
AnsiString txt="";
// setting of dat
for (i=0;i<1024;i++) txt+="bla bla bla\r\n";
// usage of dat
delete[] dat;

このコードdatにはいくつかの有用なデータが含まれており、後でtxt行を追加して作成された文字列があるため、txt数回再割り当てする必要があり、datデータが上書きされることがありますtxt(重複していなくても、AnsiString再割り当てに必要な一時txtが重複していると思いますdat)

だから私の質問は:

  1. code1、code2 で何か間違っていますか?
  2. AnsiString(再) 割り当てエラーを回避する方法はありますか? (でもまだ使ってる)

    • AnsiString大規模なデバッグの後 (質問 2 を投稿した後) 、問題を引き起こさないことがわかりました。それらは使用中にのみ発生します。本当の問題は、おそらくOpenGLクライアント間の切り替えにあります。ベクター グラフィックスのプレビューを表示する [開く/保存] ダイアログがあります。これらのVCLサブウィンドウでOpenGLの使用を無効にすると、メモリ管理エラーは完全になくなります。私は何が問題なのかはわかりません ( MFC/VCLウィンドウ間の非互換性、またはコンテキストの切り替えで間違いを犯した可能性が高いため、さらに調査します)。懸念されるOpenGLウィンドウは次のとおりです。AnsiString
    • メインVCLフォーム +クライアント領域内のOpenGLCanvas
    • メインMFCの[開く/保存] ダイアログの子 + ドッキングされたプレビューVCLフォーム +クライアント領域内のOpenGLCanvas

PS

  1. new/delete/delete[]これらのエラーは、割り当てられたサイズではなく、使用回数に依存します
  2. code1 と code2 の両方のエラーが繰り返されます (たとえば、複雑な ini ファイルをロードするパーサーがあり、ini が変更されていない場合、同じ行でエラーが発生します)。
  3. これらのエラーは、 と テンプレートを内部動的割り当てと組み合わせて使用​​する大きなプロジェクト (プレーン ソース コード > 1MB) でのみ検出されますAnsiStringが、より単純なプロジェクトでも発生する可能性がありますが、まれにしか発生しないため、見逃してしまいます。
  4. 感染したプロジェクトの仕様:
    • win32 noinstall スタンドアロン ( Win7sp1 x64を使用しますが、XPsp3 x32では同じように動作します)
    • GDIまたはOpenGl/GLSLを使用する場合は測定しません
    • デバイス ドライバーDLLを使用するかどうかを測定しません。
    • OCXなし、または非標準のVCLコンポーネント
    • DirectXなし
    • 1 バイト アラインされたコンパイル/リンク
    • RTL 、パッケージ、またはフレームワークを使用しない(スタンドアロン)

悪い英語/文法で申し訳ありません...助け/結論/提案をいただければ幸いです。

4

1 に答える 1

2

大規模なデバッグの後、問題を細かく切り分けました。bds2006 Turbo C++ のメモリ管理は、既に削除されたポインタに対して削除を呼び出そうとすると破損しました。例えば:

BYTE *dat=new BYTE[10],*tmp=dat;
delete[] dat;
delete[] tmp;

この後、メモリ管理は信頼できません。('new' は既に割り当てられたスペースを割り当てることができます)

もちろん、同じポインターを 2 回削除することはプログラマー側のバグですが、この問題を発生させるすべての問題の本当の原因を見つけました (ソース コードに明らかなバグはありません)。次のコードを参照してください。

//---------------------------------------------------------------------------
class test
    {
public:
    int siz;
    BYTE *dat;
    test()
        {
        siz=10;
        dat=new BYTE[siz];
        }
    ~test()
        {
        delete[] dat;   // <- add breakpoint here
        siz=0;
        dat=NULL;
        }
    test& operator = (const test& x)
        {
        int i;
        for (i=0;i<siz;i++) if (i<x.siz) dat[i]=x.dat[i];
        for (   ;i<siz;i++) dat[i]=0;
        return *this;
        }
    };
//---------------------------------------------------------------------------
test get()
    {
    test a;
    return a;   // here call a.~test();
    }           // here second call a.~test(); 
//---------------------------------------------------------------------------
void main()
    {
    get();
    }
//---------------------------------------------------------------------------

In functionget()は、クラス a のデストラクタを 2 回呼び出します。コンストラクターを作成するのを忘れたため、実際に 1 回とそのコピー用に 1 回

test::test(test &x);

[Edit1] コードのさらなるアップグレード

わかりました。さらに多くのバグ ケースを修正するために、クラスと構造体の両方のテンプレートの初期化コードを改良しました。このコードを構造体/クラス/テンプレートに追加し、必要に応じて機能を追加します

T()     {}
T(T& a) { *this=a; }
~T()    {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
  • T構造体/クラス名です
  • 最後の演算子は、そのT中で動的割り当てを使用する場合にのみ必要です。割り当てが使用されていない場合は、そのままにしておくことができます

これにより、次のような他のコンパイラの問題も解決されます。

誰かが同様の問題を抱えている場合、これが役立つことを願っています。

また、メモリ割り当てをデバッグする必要がある場合は、C++ コードのポインタのトレースバックも参照してください...mmap

于 2013-08-02T12:00:51.300 に答える