web-dev-qa-db-ja.com

Cのスマートポインタ/安全なメモリ管理?

私や他の多くの人は、スマートポインターを使用して、RAIIなどを使用して、C++での安全でないメモリ操作をまとめることで大きな成功を収めていると思います。ただし、デストラクタ、クラス、演算子のオーバーロードなどがある場合は、ラッピングメモリ管理を実装する方が簡単です。

生のC99で書いている人にとって、安全なメモリ管理を支援するためにどこを指すことができますか(しゃれは意図されていません)?

ありがとう。

42
mannicken

質問は少し古いですが、私は自分の スマートポインタライブラリ for GNUコンパイラ(GCC、Clang、ICC、MinGW、 ...)。

この実装は、クリーンアップ変数属性a GNU拡張子に依存して、スコープ外に出たときにメモリを自動的に解放するため、notISO C99、ただしGNU拡張子を持つC99。

例:

simple1.c:

#include <stdio.h>
#include <csptr/smart_ptr.h>

int main(void) {
    smart int *some_int = unique_ptr(int, 1);

    printf("%p = %d\n", some_int, *some_int);

    // some_int is destroyed here
    return 0;
}

コンパイルとValgrindセッション:

$ gcc -std=gnu99 -o simple1 simple1.c -lcsptr
$ valgrind ./simple1
==3407== Memcheck, a memory error detector
==3407== Copyright (C) 2002-2013, and GNU GPL\'d, by Julian Seward et al.
==3407== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==3407== Command: ./simple1 
==3407==
0x53db068 = 1
==3407==
==3407== HEAP SUMMARY:
==3407==     in use at exit: 0 bytes in 0 blocks
==3407==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==3407==
==3407== All heap blocks were freed -- no leaks are possible
==3407==
==3407== For counts of detected and suppressed errors, rerun with: -v
==3407== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
19
Snaipe

使用法をバックアップするための言語構文がないため、rawCでスマートポインターを処理することは困難です。私が見たほとんどの試みは実際には機能しません。オブジェクトがスコープを離れるときにデストラクタが実行されるという利点がないためです。これがスマートポインタを機能させる理由です。

これについて本当に心配している場合は、 ガベージコレクター を直接使用し、スマートポインターの要件を完全にバイパスすることを検討することをお勧めします。

13
Reed Copsey

検討したいもう1つのアプローチは、 Apacheが使用する であるプールメモリアプローチです。これは、リクエストまたはその他の短期間のオブジェクトに関連付けられている動的なメモリ使用量がある場合に非常にうまく機能します。リクエスト構造にプールを作成し、常にプールからメモリを割り当て、リクエストの処理が完了したらプールを解放するようにすることができます。少し使ってみると、それほどパワフルに聞こえません。 RAIIとほぼ同じくらいいいです。

8
D.Shawley

splintGimpel PC-Lintなどの静的コード分析ツールがここで役立つ場合があります-自動「継続的インテグレーション」に配線することで、これらを適度に「予防的」にすることもできます。スタイルビルドサーバー。 (あなたはそれらの1つを持っていますよね?:grin :)

このテーマには他の(より高価な)バリアントもあります...

3
leander

必要な構文が提供されていないため、Cでスマートポインターを実行することはできませんが、練習すればリークを回避できます。リソースリリースコードは、割り当てた直後に記述してください。したがって、mallocを書き込むときはいつでも、対応するfreeをクリーンアップセクションにすぐに書き込む必要があります。

Cでは、「GOTOクリーンアップ」パターンがよく見られます。

int foo()
{
    int *resource = malloc(1000);
    int retVal = 0;
    //...
    if (time_to_exit())
    {
        retVal = 123;
        goto cleanup;
    }
cleanup:
    free(resource);
    return retVal;
}

Cでは、ものを割り当てる多くのコンテキストも使用します。同じルールをそれに適用することもできます。

int initializeStuff(Stuff *stuff)
{
    stuff->resource = malloc(sizeof(Resource));
    if (!stuff->resource) 
    {
        return -1; ///< Fail.
    }
    return 0; ///< Success.
}

void cleanupStuff(Stuff *stuff)
{
    free(stuff->resource);
}

これは、オブジェクトのコンストラクタとデストラクタに類似しています。割り当てられたリソースを他のオブジェクトに渡さない限り、リークしたり、ポインタがぶら下がったりすることはありません。

割り当てを追跡し、リークしているブロックatexitを書き込むカスタムアロケータを作成することは難しくありません。

割り当てられたリソースへのポインターを提供する必要がある場合は、そのラッパーコンテキストを作成でき、各オブジェクトはリソースの代わりにラッパーコンテキストを所有します。これらのラッパーは、リソースとカウンターオブジェクトを共有します。カウンターオブジェクトは、使用状況を追跡し、誰も使用しない場合にオブジェクトを解放します。これがC++ 11のshared_ptrおよびweak_ptr動作します。ここに詳しく書かれています: weak_ptrはどのように機能しますか?

3
Calmarius

中括弧の代わりに使用するマクロ(BEGINやENDなど)を定義して、スコープを終了するリソースの自動破棄をトリガーできます。これには、そのようなすべてのリソースが、オブジェクトのデストラクタへのポインタも含むスマートポインタによってポイントされている必要があります。私の実装では、スマートポインターのスタックをヒープメモリに保持し、スコープの入り口でスタックポインターを記憶し、スコープの出口で記憶されたスタックポインターの上のすべてのリソースのデストラクタを呼び出します(ENDまたはリターンのマクロ置換)。これは、setjmp/longjmp例外メカニズムが使用されている場合でもうまく機能し、catch-blockと例外がスローされたスコープの間のすべての中間スコープもクリーンアップします。実装については、 https://github.com/psevon/exceptions-and-raii-in-c.git を参照してください。

2
user3510229

Win32でコーディングしている場合は、 構造化例外処理 を使用して同様のことを実行できる可能性があります。あなたはこのようなことをすることができます:

foo() {
    myType pFoo = 0;
    __try
    {
        pFoo = malloc(sizeof myType);
        // do some stuff
    }
    __finally
    {
        free pFoo;
    }
}

RAIIほど簡単ではありませんが、すべてのクリーンアップコードを一か所に集めて、確実に実行することができます。

1
Steve Rowe
Sometimes i use this approach and it seems good :)

Object *construct(type arg, ...){

    Object *__local = malloc(sizeof(Object));
    if(!__local)
        return NULL;
    __local->prop_a = arg;
    /* blah blah */


} // constructor

void destruct(Object *__this){

   if(__this->prop_a)free(this->prop_a);
   if(__this->prop_b)free(this->prop_b);

} // destructor

Object *o = __construct(200);
if(o != NULL)
   ;;

// use

destruct(o);

/*
  done !
*/
0
Tiger