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