15

私はプラグイン アーキテクチャを持っています。動的ライブラリで関数を呼び出すとchar*、答えが返され、後の段階で使用されます。

これはプラグイン関数のシグネチャです:

char* execute(ALLOCATION_BEHAVIOR* free_returned_value, unsigned int* length);

ここで、プラグイン (ライブラリ内) は、返されたばかりの文字列をプラグインがどのように割り当てたかを教えてくれALLOCATION_BEHAVIORます。戻り値を解放するために使用する必要があり、メモリリークを取り除くために使用する必要があることを教えてくれます。DO_NOT_FREE_MEFREE_MEDELETE_MEDO_NOT_FREE_MEconst static char*FREE_MEfree()DELETE_MEdelete[]

free()明らかに、私はプラグインを信頼していないので、彼が変数に私に言った場合、実際にそれは本当に解放できるものであることを確認できるようにしたいと思います...これは今日のC/C++テクノロジーを使用して可能ですか? Linux/Windows では?

4

7 に答える 7

11

malloc/freeとを区別するnew/deleteことは一般的に不可能であり、少なくとも信頼できる方法や移植性のある方法では不可能です。とにかく、多くの実装でnew単にラップするだけです。malloc

ヒープ/スタックを区別する次の代替手段はいずれもテストされていませんが、すべて機能するはずです。

Linux:

  1. Luca Tettananti によって提案されたソリューションは、解析/proc/self/mapsしてスタックのアドレス範囲を取得します。
  2. 起動時の最初cloneのプロセスとして、これはスタックを提供することを意味します。あなたがそれを提供するので、あなたはそれがどこにあるかを自動的に知っています。
  3. 0 が返されるまでlevel__builtin_frame_addressパラメータを増やしてGCC の関数を呼び出します。これで深さがわかります。ここで、最大レベルでもう一度呼び出し、レベル 0 で 1 回呼び出します。スタック上に存在するものはすべて、必ずこれら 2 つのアドレスの間にある必要があります。__builtin_frame_address
  4. sbrk(0)起動時の最初のこととして、値を覚えておいてください。何かがヒープ上にあるかどうかを知りたいときはいつでも、ヒープ上にあるものはsbrk(0)2 つの値の間にある必要があります。これは、大規模な割り当てにメモリ マッピングを使用するアロケータでは確実に機能しないことに注意してください。

スタックの場所とサイズ (代替案 1 と 2) がわかれば、アドレスがその範囲内にあるかどうかを調べるのは簡単です。そうでない場合は、必然的に「ヒープ」になります(誰かが超賢くなろうとして、静的グローバルへのポインター、または関数ポインターなどを提供しない限り...)。

ウィンドウズ:

  1. CaptureStackBackTraceを使用します。スタック上に存在するものはすべて、返されたポインター配列の最初と最後の要素の間にある必要があります。
  2. 上記のようにGCC-MinGW(および__builtin_frame_address、これはうまくいくはずです)を使用します。
  3. GetProcessHeapsおよびを使用しHeapWalkて、割り当てられたすべてのブロックが一致するかどうかを確認します。ヒープのどれにも一致しない場合、結果としてスタックに割り当てられます (... または、誰かがあなたと非常に賢くしようとする場合はメモリ マッピング)。
  4. とまったく同じサイズで使用HeapReAllocしてください。HEAP_REALLOC_IN_PLACE_ONLYこれが失敗した場合、指定されたアドレスから始まるメモリ ブロックはヒープに割り当てられません。「成功」した場合はノーオペレーションです。
  5. 使用GetCurrentThreadStackLimits(Windows 8 / 2012 のみ)
  6. 返された TEBのフィールドとフィールドを呼び出しNtCurrentTeb()(または読み取りfs:[18h]) 使用します。StackBaseStackLimit
于 2013-05-23T12:03:46.000 に答える
8

私は数年前に comp.lang.c で同じ質問をしました。James Kuyper の反応が気に入りました。

はい。割り当てるときは、それを追跡します。

これを行う方法は、メモリの所有権の概念を使用することです。割り当てられたメモリのブロックの存続期間中は常に、そのブロックを「所有する」唯一のポインタを持つ必要があります。他のポインターがそのブロックを指している可能性がありますが、所有しているポインターのみを free() に渡す必要があります。

可能であれば、所有ポインターは、ポインターを所有する目的で予約する必要があります。所有していないメモリへのポインターを格納するために使用しないでください。私は通常、malloc(); の呼び出しで所有ポインタが初期化されるように調整しようとします。それが不可能な場合は、最初に使用する前に NULL に設定する必要があります。また、所有しているメモリを free() した直後に、所有しているポインターの有効期間が終了するようにします。ただし、それが不可能な場合は、そのメモリを free() した直後に NULL に設定してください。これらの予防措置を講じた上で、最初に free() に渡すことなく、null 以外の所有ポインターの存続期間を終了させて​​はなりません。

どのポインターが「所有」ポインターであるかを追跡するのに問題がある場合は、宣言の横にその事実に関するコメントを入れてください。問題が多い場合は、命名規則を使用してこの機能を追跡してください。

なんらかの理由で、所有ポインター変数をそれが指すメモリーの所有権専用に予約することができない場合は、別のフラグ変数を確保して、そのポインターが現在そのポインターが指すメモリーを所有しているかどうかを追跡する必要があります。 . ポインターと所有権フラグの両方を含む構造体を作成することは、これを処理するための非常に自然な方法です。これにより、それらが分離されないことが保証されます。

かなり複雑なプログラムを使用している場合は、メモリの所有権をあるポインタ変数から別のポインタ変数に移す必要があるかもしれません。その場合は、ターゲット ポインターが所有するすべてのメモリが転送前に free() されていることを確認し、ソース ポインターの有効期間が転送直後に終了しない限り、ソース ポインターを NULL に設定します。所有権フラグを使用している場合は、それに応じてリセットしてください。

于 2013-05-23T09:27:12.817 に答える
2

Linux では、解析/proc/self/mapsしてスタックとヒープの場所を抽出し、ポインターが範囲のいずれかに該当するかどうかを確認できます。

ただし、メモリを解放または削除で処理する必要があるかどうかはわかりません。アーキテクチャを制御する場合、プラグインが割り当てられたメモリを解放し、適切な API (IOW、plugin_freeあなたの と対称的な関数execute) を追加することができます。別の一般的なパターンは、呼び出しごとにプラグインに渡され、クリーンアップを行うためにシャットダウン時にプラグインによって使用されるコンテキスト オブジェクト (初期化時に作成される) で割り当てを追跡することです。

于 2013-05-23T09:29:51.887 に答える
-1

ポインターがスタック上にあるかヒープ上にあるかを判断するには、いくつかのデバッグ ツールを使用する必要があります。Windows では、Sysinternals Suite をダウンロードします。これにより、デバッグ用のさまざまなツールが提供されます。

于 2013-05-23T09:36:34.570 に答える