3

いくつかのつらい経験を経て、ダングリング ポインターとダブル フリーの問題が理解できました。私は適切な解決策を求めています。

aStruct他の配列を含むいくつかのフィールドがあります。

aStruct *A = NULL, *B = NULL;
A = (aStruct*) calloc(1, sizeof(sStruct));
B = A;
free_aStruct(A);
...
// Bunch of other code in various places.
...
free_aStruct(B);

正常に終了するfree_aStruct(X)ように書く方法はありますか?free_aStruct(B)

void free_aStruct(aStruct *X) {
    if (X ! = NULL) {
        if (X->a != NULL) { free(X->a); x->a = NULL; }
        free(X); X = NULL;
    }
}

上記を実行すると、が呼び出されたA = NULLときにのみ設定されます。がぶら下がっています。free_aStruct(A);B

この状況をどのように回避/改善できますか? 参照カウントは唯一の実行可能な解決策ですか? free_aStruct(B);または、爆発を防ぐために、メモリを解放するための他の「防御的な」アプローチはありますか?

4

5 に答える 5

5

単純な C では、この問題の最も重要な解決策は規律です。問題の根本はここにあるからです。

B = A;

構造体内で何も変更せずにポインターのコピーを作成し、コンパイラーからの警告なしに使用するものを回避します。次のようなものを使用する必要があります。

B = getref_aStruct(A);

次に重要なことは、割り当てを追跡することです。クリーンなモジュール化、情報の隠蔽、および DRY が役立つことがあります -- 繰り返さないでください。free_aStruct() 関数を使用してメモリを解放している間に、 calloc() を直接呼び出してメモリを割り当てます。create_aStruct() を使用して割り当てることをお勧めします。これにより、コードベース全体にメモリ割り当てを投げる代わりに、物事を一元化して 1 か所だけに保つことができます。

これは、この上に構築するメモリ追跡システムのより優れたベースです。

于 2010-04-01T20:42:10.833 に答える
2

Cはメモリを管理する責任と負担をあなたに課すため、これを自動的に行うことはできないと思います。したがって、参照ともちろんダングリングポインターが確実に処理されるようにする責任があります!

void free_aStruct(aStruct *X){
  if (X ! = NULL){
      if (X->a != NULL){free(X->a); x->a = NULL;}
      無料 (X); X = NULL;
}
}

ところで、if上記のチェックにはタイプミスがあります...「X」の代わりに小文字の「x」を使用しています...

上記のコードを見たときの私の考えは、タイプ のポインター変数のコピーに対して解放を行っているということですaStruct *。代わりに、参照渡しになるように変更します...

void free_aStruct(aStruct **X){
  if (*X ! = NULL){
      if (*X->a != NULL){
          free(*X->a);
          *X->a = NULL;
      }
      フリー(*X);
      *X = NULL;
  }
}

そして、次のように呼び出します。

free_aStruct(&A);

それ以外は、意図しないコーディングであろうと設計上の欠陥であろうと、最終的に「ダングリングポインター」の責任は自分にあります...

于 2010-04-01T20:38:51.980 に答える
1

free_aStruct(B) が爆発するのを防ぐことができたとしても、コメントの背後にあるコードに B への参照がある場合、それは解放されたメモリを使用することになるため、いつでも新しいデータで上書きされる可能性があります。無料通話を「修正」するだけでは、根本的なエラーが隠されるだけです。

于 2010-04-01T20:28:39.640 に答える
1

使用できるテクニックはありますが、肝心なのは、C で厳密に強制できるものは何もないということです。代わりに、valgrind (またはpurify) を開発プロセスに組み込むことをお勧めします。また、一部の静的コード アナライザーは、これらの問題の一部を検出できる場合があります。

于 2010-04-01T21:16:01.007 に答える
1

参照カウントはそれほど難しくありません。

aStruct *astruct_getref(aStruct *m)
{
    m->refs++;
    return m;
}

aStruct *astruct_new(void)
{
    sStruct *new = calloc(1, sizeof *new);
    return astruct_getref(new);
}

void astruct_free(aStruct *m)
{
    if (--m->refs == 0)
        free(m);
}

(マルチスレッド環境では、ロックを追加する必要がある場合もあります)。

次に、コードは次のようになります。

aStruct *A = NULL, *B = NULL;
A = astruct_new();
B = astruct_getref(A);
astruct_free(A);
...
//bunch of other code in various places.
...
astruct_free(B);

ロックについて質問しました。残念ながら、ロックに関して万能の答えはありません。それはすべて、アプリケーションにどのようなアクセス パターンがあるかによって異なります。入念な設計と深い思考に勝るものはありません。(たとえば、どのスレッドも呼び出したり、別のスレッドのを呼び出したりしないことが保証できる場合、参照カウントを保護する必要はまったくありません。上記の単純な実装で十分です)。astruct_getref()astruct_free()aStruct

とはいえ、上記のプリミティブは、astruct_getref()およびastruct_free()関数への同時アクセスをサポートするように簡単に拡張できます。

aStruct *astruct_getref(aStruct *m)
{
    mutex_lock(m->reflock);
    m->refs++;
    mutex_unlock(m->reflock);
    return m;
}

aStruct *astruct_new(void)
{
    sStruct *new = calloc(1, sizeof *new);
    mutex_init(new->reflock);
    return astruct_getref(new);
}

void astruct_free(aStruct *m)
{
    int refs;

    mutex_lock(m->reflock);
    refs = --m->refs;
    mutex_unlock(m->reflock);
    if (refs == 0)
        free(m);
}

...ただし、同時アクセスの対象となる構造体へのポインターを含む変数には、独自のロックも必要になることに注意してください (たとえば、aStruct *foo同時にアクセスされるグローバルがある場合は、付随するfoo_lock.

于 2010-04-02T01:17:25.503 に答える