C プログラミングでは、任意の種類のポインターを引数として free に渡すことができますが、free に割り当てられたメモリのサイズをどのように知るのでしょうか? 関数にポインターを渡すときはいつでも、サイズも渡す必要があります (つまり、10 要素の配列は、配列のサイズを知るためにパラメーターとして 10 を受け取る必要があります)。無料機能。配列の長さの余分な変数をカートに入れる必要がないように、独自の関数でこれと同じ手法を使用できますか?
11 に答える
を呼び出すときはmalloc()
、割り当てるメモリの量を指定します。実際に使用されるメモリの量はこれよりわずかに多く、(少なくとも) ブロックの大きさを記録する追加情報が含まれています。他の情報に(確実に)アクセスすることはできません-また、そうすべきではありません:-)。
を呼び出すとfree()
、単に追加情報を調べて、ブロックの大きさを調べます。
C メモリ割り当て関数のほとんどの実装では、各ブロックのアカウンティング情報をインラインまたは個別に格納します。
典型的な方法 (インライン) の 1 つは、要求したヘッダーとメモリの両方を実際に割り当て、最小サイズまでパディングすることです。たとえば、20 バイトを要求した場合、システムは 48 バイトのブロックを割り当てる可能性があります。
- サイズ、特殊マーカー、チェックサム、次/前のブロックへのポインターなどを含む 16 バイトのヘッダー。
- 32 バイトのデータ領域 (16 の倍数になるように 20 バイトがパディングされます)。
このとき与えられるアドレスは、データ領域のアドレスです。次に、ブロックを解放するfree
と、指定したアドレスが取得され、そのアドレスまたはその周りのメモリがいっぱいになっていないと仮定して、その直前のアカウンティング情報がチェックされます。グラフィカルに、それは次のようになります。
____ The allocated block ____
/ \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
^
|
+-- The address you are given
ヘッダーとパディングのサイズは完全に実装で定義されていることに注意してください (実際には、全体が実装で定義されています(a)が、インライン アカウンティング オプションは一般的なものです)。
アカウンティング情報に存在するチェックサムと特殊なマーカーは、それらを上書きしたり、2 回解放したりすると、「メモリ アリーナが壊れています」や「二重解放」などのエラーの原因となることがよくあります。
パディング(割り当てをより効率的にするため)は、問題を引き起こすことなく、要求されたスペースの終わりを少し超えて書き込むことができる場合がある理由です(それでも、そうしないでください。これは未定義の動作であり、時々機能するという理由だけで機能しません.やってもいいという意味です)。
(a)malloc
128 バイト以下を要求したと仮定して (それ以上の要求は戻り値が NULL になる可能性があります)。128 バイトのチャンクが割り当てられているかどうかを判断するために、非常に単純なビットマスク (つまり、インラインではない) が使用されていました。
私が開発した他のものには、16 バイト チャンク、64 バイト チャンク、256 バイト チャンク、1K チャンク用の異なるプールがあり、ここでもビットマスクを使用して、どのブロックが使用または利用可能かを決定していました。
これらのオプションは両方とも、アカウンティング情報のオーバーヘッドを削減し、malloc
andの速度を向上させましたfree
(解放時に隣接するブロックを結合する必要はありません)。これは、私たちが作業していた環境では特に重要です.
comp.lang.c
FAQ リストから: free は解放するバイト数をどうやって知るのですか?
malloc/free の実装は、割り当てられた各ブロックのサイズを記憶しているため、解放時にサイズを思い出す必要はありません。(通常、サイズは割り当てられたブロックに隣接して格納されます。これが、割り当てられたブロックの境界が少しでもオーバーステップされた場合、通常、物事がひどく壊れる理由です)
この回答は、free() が割り当て解除するメモリの量をどのように知っているのですか? から移動されました。明らかな重複した質問により、私は突然回答を妨げられました。この回答は、この複製に関連する必要があります。
の場合malloc
、ヒープ アロケーターは、元の返されたポインターのマッピングを、free
後でメモリを ing するために必要な関連する詳細に格納します。これには通常、使用中のアロケーターに関連する形式でメモリ領域のサイズを格納することが含まれます。たとえば、生のサイズ、割り当てを追跡するために使用されるバイナリ ツリー内のノード、使用中のメモリ「ユニット」の数などです。
free
ポインターを「名前変更」したり、何らかの方法で複製したりしても失敗しません。ただし、参照はカウントされず、最初のものだけfree
が正しいでしょう。追加free
の s は「ダブル フリー」エラーです。
free
以前の によって返された値とは異なる値を持ち、まだ解放されていないポインターを使用しようとするmalloc
と、エラーになります。から返されたメモリ領域を部分的に解放することはできませんmalloc
。
関連するメモとして、GLibライブラリには、暗黙的なサイズを保存しないメモリ割り当て関数があります。次に、size パラメータを free に渡すだけです。これにより、オーバーヘッドの一部を削減できます。
ヒープ マネージャは、 を呼び出したときに、割り当てられたブロックに属するメモリの量をどこかに保存しましたmalloc
。
自分で実装したことはありませんが、割り当てられたブロックの直前のメモリにメタ情報が含まれている可能性があります。
malloc()
システム/コンパイラにfree()
依存するため、特定の答えを出すのは困難です。
この他の質問の詳細。
元の手法は、少し大きめのブロックを割り当ててサイズを最初に保存し、その後アプリケーションにブログの残りを与えるというものでした。余分なスペースにはサイズが保持され、再利用のために空きブロックをスレッド化するためのリンクが含まれている可能性があります。
ただし、これらのトリックには、キャッシュやメモリ管理の動作の悪さなど、特定の問題があります。ブロック内でメモリを使用すると、不必要にページインされる傾向があり、共有とコピーオンライトを複雑にするダーティ ページも作成されます。
したがって、より高度な手法は、別のディレクトリを保持することです。メモリの領域が同じ 2 のべき乗サイズを使用するエキゾチックなアプローチも開発されています。
一般に、答えは次のとおりです。状態を保持するために別のデータ構造が割り当てられます。
あなたの質問の後半に答えるために:はい、できます、そしてCのかなり一般的なパターンは次のとおりです:
typedef struct {
size_t numElements
int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;
#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
malloc を呼び出すと、その要件からより多くのバイトが消費されます。このより多くのバイト消費には、チェックサム、サイズ、その他の追加情報などの情報が含まれています。その時点で free を呼び出すと、その追加情報に直接移動し、アドレスを見つけて、どれだけのブロックが解放されるかを見つけます。