web-dev-qa-db-ja.com

メモリの割り当て/割り当て解除?

私は最近、メモリの割り当てを見てきましたが、基本について少し混乱しています。私は単純なものに頭を包むことができませんでした。メモリを割り当てるとはどういう意味ですか?何が起こるのですか?これらの質問への回答をいただければ幸いです。

  1. 割り当てられている「メモリ」はどこにありますか?
  2. この「記憶」とは何ですか?配列内のスペース?または、他の何か?
  3. この「メモリ」が割り当てられるとどうなりますか?
  4. メモリが解放されるとどうなりますか?
  5. また、次のC++行でmallocが何をするのかを誰かが答えてくれれば、本当に助かります。

    char* x; 
    x = (char*) malloc (8);
    

ありがとうございました。

36
Isaac

メモリモデル

C++標準にはメモリモデルがあります。一般的な方法でコンピューターシステムのメモリをモデル化しようとします。標準では、バイトはメモリモデルのストレージユニットであり、メモリはバイトで構成されていると定義しています(§1.7)。

C++メモリモデルの基本的なストレージユニットはバイトです。 [...] C++プログラムで使用可能なメモリは、連続したバイトの1つ以上のシーケンスで構成されます。

オブジェクトモデル

標準は常にオブジェクトモデルを提供します。これは、オブジェクトがストレージの領域であることを指定します(したがって、オブジェクトはバイトで構成され、メモリに常駐します)(§1.8):

C++プログラムの構成体は、オブジェクトを作成、破棄、参照、アクセス、および操作します。オブジェクトはストレージの領域です。

さあ、行きます。メモリはオブジェクトが保存される場所です。オブジェクトをメモリに保存するには、必要なストレージ領域を割り当てる必要があります。

割り当ておよび割り当て解除関数

この標準は、暗黙的に宣言された2つのグローバルスコープ割り当て関数を提供します。

_void* operator new(std::size_t);
void* operator new[](std::size_t);
_

これらの実装方法は標準の関心事ではありません。重要なのは、渡された引数に対応するバイト数でストレージの領域へのポインタを返すことです(§3.7.4.1):

割り当て機能は、要求された量のストレージを割り当てようとします。成功した場合、バイト単位の長さが少なくとも要求されたサイズと同じであるストレージブロックの開始アドレスを返します。割り振り機能から戻ったときに割り振られたストレージの内容に制約はありません。

また、対応する2つの割り当て解除関数も定義します。

_void operator delete(void*);
void operator delete[](void*);
_

以前に割り当てられたストレージの割り当てを解除するために定義されています(§3.7.4.2):

標準ライブラリ内の割り当て解除関数に与えられた引数がNULLポインター値(4.10)ではないポインターである場合、割り当て解除関数は、ポインターによって参照されるストレージの割り当てを解除し、割り当て解除されたストレージの一部を参照するすべてのポインターを無効にします。

newおよびdelete

通常、初期化されていないメモリのみを提供するため、割り当て関数と割り当て解除関数を直接使用する必要はありません。代わりに、C++では、newおよびdeleteを使用してオブジェクトを動的に割り当てる必要があります。 new-expressionは、上記の割り当て関数のいずれかを使用して、要求されたタイプのストレージを取得し、そのオブジェクトを何らかの方法で初期化します。たとえば、new int()intオブジェクトにスペースを割り当ててから、0に初期化します。§5.3.4を参照してください。

New-expressionは、割り当て関数を呼び出すことにより、オブジェクトのストレージを取得します(3.7.4.1)。

[...]

タイプTのオブジェクトを作成するnew-expressionは、そのオブジェクトを初期化します[...]

逆の場合、deleteはオブジェクトのデストラクタ(存在する場合)を呼び出し、ストレージの割り当てを解除します(§5.3.5):

delete-expressionのオペランドの値がNULLポインター値でない場合、delete-expression削除されるオブジェクトまたは配列の要素のデストラクタ(存在する場合)を呼び出します。

[...]

delete-expressionのオペランドの値がNULLポインター値でない場合、delete-expressionは、割り当て解除関数を呼び出します(3.7.4.2)。

その他の割り当て

ただし、これらはストレージの割り当てまたは割り当て解除の唯一の方法ではありません。言語の多くの構成体は、ストレージの割り当てを暗黙的に必要とします。たとえば、_int a;_などのオブジェクト定義を指定するには、ストレージも必要です(§7):

定義により、適切な量のストレージが予約され、適切な初期化(8.5)が行われます。

C標準ライブラリ:mallocおよびfree

さらに、_<cstdlib>_ヘッダーは_mallocおよびfree関数を含む_stdlib.h_ C標準ライブラリの内容を取り込みます。これらは、C++標準で定義された割り当ておよび割り当て解除関数と同様に、C標準で定義され、メモリの割り当てと割り当て解除を行います。 mallocの定義は次のとおりです(C99§7.20.3.3):

void *malloc(size_t size);
説明
malloc関数は、サイズがsizeで指定され、値が不定であるオブジェクトにスペースを割り当てます。
返品
malloc関数は、nullポインターまたは割り当てられたスペースへのポインターを返します。

freeの定義(C99§7.20.3.2):

void free(void *ptr);
説明
free関数は、ptrが指すスペースの割り当てを解除します。つまり、さらに割り当てられるようにします。 ptrがNULLポインターの場合、アクションは発生しません。それ以外の場合、引数がcallocmalloc、またはrealloc関数によって以前に返されたポインターと一致しない場合、またはfreeまたはreallocの呼び出しによってスペースが割り当て解除された場合、動作は未定義です。

ただし、C++でmallocfreeを使用する良い言い訳はありません。前に説明したように、C++には独自の選択肢があります。


質問への回答

質問に直接答えるには:

  1. 割り当てられている「メモリ」はどこにありますか?

    C++標準は気にしません。それは単に、プログラムがバイトで構成されているメモリを持っていると言います。このメモリは割り当てることができます。

  2. この「記憶」とは何ですか?配列内のスペース?または、他の何か?

    標準に関する限り、メモリは単なるバイトのシーケンスです。これは、標準が典型的なコンピューターシステムmodelのみを試みるため、意図的に非常に汎用的です。ほとんどの場合、コンピューターのRAMのモデルと考えることができます。

  3. この「メモリ」が割り当てられるとどうなりますか?

    メモリーを割り当てると、プログラムで使用できるストレージの領域が作成されます。オブジェクトは割り当てられたメモリで初期化されます。知っておく必要があるのは、メモリを割り当てることができるということだけです。プロセスへの物理メモリの実際の割り当ては、オペレーティングシステムによって行われる傾向があります。

  4. メモリが解放されるとどうなりますか?

    以前に割り当てられたメモリの割り当てを解除すると、そのメモリはプログラムで使用できなくなります。割り当て解除されたストレージになります。

  5. また、次のC++行でmallocが何をするのかを誰かが答えてくれれば、本当に助かります。

    _char* x; 
    x = (char*) malloc (8);
    _

    ここで、mallocは単純に8バイトのメモリを割り当てています。返されるポインターは_char*_にキャストされ、xに格納されます。

60

1)割り当てられている「メモリ」はどこですか?

これは、オペレーティングシステム、プログラミング環境(gcc対Visual C++対Borland C++対その他)、コンピューター、使用可能なメモリなどに基づいて完全に異なります。一般に、メモリは、いわゆるヒープ、待機中のメモリ領域から割り当てられます。あなたが使用するための周り。通常、使用可能なRAMを使用します。しかし、常に例外があります。ほとんどの場合、それが記憶を与えてくれる限り、それがどこから来たのかは大きな関心事ではありません。仮想メモリなどの特別な種類のメモリがあり、実際にはRAMである場合とない場合があり、ハードドライブ(または同様のストレージデバイス)に移動する場合があります。実際のメモリが不足しています。完全な説明は非常に長くなります。

2)この「メモリ」とは何ですか?配列内のスペース?または他に何か?

メモリは通常、コンピュータのRAMです。メモリを巨大な「配列」と考えると便利な場合、特定のメモリのように動作し、大量のバイト(8 unsigned char値によく似たビット値)。メモリの一番下のインデックス0から始まりますが、前と同じように、ここには多くの例外があり、メモリの一部はハードウェアにマッピングされます。まったく存在しないかもしれません!

)この「メモリ」が割り当てられたときに正確に何が起こりますか?

いつでも、ソフトウェアの割り当てに利用できるものがあるはずです(本当に期待しています!)。割り当て方法はシステムに大きく依存します。一般に、メモリの領域が割り当てられ、アロケータがそれを使用済みとしてマークし、その後、システムのすべてのメモリのどこにメモリがあるかをプログラムに伝えるためのポインタが使用者に与えられます。あなたの例では、プログラムは8バイト(char)の連続ブロックを見つけ、「使用中」としてマークした後、そのブロックを見つけた場所へのポインタを返します。

4)メモリの割り当てが解除されるとどうなりますか?

システムは、そのメモリを再び使用可能としてマークします。これは非常に複雑です。なぜなら、これはしばしばメモリに穴を開けるからです。 8バイトを割り当て、さらに8バイトを割り当て、最初の8バイトの割り当てを解除すると、穴ができます。割り当て解除、メモリ割り当てなどの処理について書かれた本全体があります。したがって、うまくいけば短い答えで十分でしょう。

5)また、誰かがこれらのC++行でmallocの動作に答えることができれば、本当に役立ちます:

本当に大雑把に、それが関数内にあると仮定します(ところで、メモリの割り当てを解除せず、メモリリークが発生するため、これを実行しないでください)。

void mysample() {
  char *x; // 1
  x = (char *) malloc(8); // 2
}

1)これは、ローカルスタックスペースに予約されているポインターです。初期化されていないため、メモリのそのビットが持っていたものを指します。

2)8のパラメーターでmallocを呼び出します。キャストは、型が適用されていないことを意味する(void *)を返すため、C/C++に(char *)にすることを知らせます。次に、結果のポインターがx変数に格納されます。

非常に粗雑なx86 32ビットアセンブリでは、これは漠然と次のようになります。

PROC mysample:
  ; char *x;
  x = DWord Ptr [ebp - 4]
  enter 4, 0   ; Enter and preserve 4 bytes for use with 

  ; x = (char *) malloc(8);
  Push 8       ; We're using 8 for Malloc
  call malloc  ; Call malloc to do it's thing
  sub esp, 4   ; Correct the stack
  mov x, eax   ; Store the return value, which is in EAX, into x

  leave
  ret

実際の割り当てについては、ポイント3で漠然と説明しています。mallocは、通常、残りのすべてを処理するシステム関数を呼び出します。他のすべてと同様に、OSごと、システムごとなどで大きく異なります。

11
Mark Ormston

1割り当てられている「メモリ」はどこにありますか?

言語の観点からは、これは指定されていません。ほとんどの場合、細かい詳細は重要ではありません。また、_C++_標準は、不必要な制限を最小限に抑えるために、ハードウェアの詳細を不十分に指定する側で誤りがちです(コンパイラーが実行可能なプラットフォームと最適化の両方で)。

sftrabbitの答えは、物事のこの終わりの素晴らしい概要を提供します(そして、それはあなたが本当に必要とするすべてです)、しかし、私はそれが助けとなる場合にいくつかの実際の例を与えることができます。

例1:

十分に古いシングルユーザーコンピューター(または十分に小さい組み込みコンピューター)では、ほとんどの物理的なRAMがプログラムで直接利用できる場合があります。このシナリオでは、mallocを呼び出します。またはnewは本質的に内部のブックキーピングであり、ランタイムライブラリはそのRAMが現在使用されているチャンクを追跡できます。これを手動で行うことはできますが、かなり面倒です早く。

例2:

最新のマルチタスクオペレーティングシステムでは、物理的なRAMはカーネルスレッドを含む多くのプロセスや他のタスクと共有されます。バックグラウンドでのディスクキャッシングとI/Oバッファリングにも使用されます。データが使用されていないときにデータをディスク(または他のストレージデバイス)にスワップできる仮想メモリサブシステム。

このシナリオでは、newを呼び出すと、最初にプロセスに内部的に十分な空き領域があるかどうかを最初に確認し、ない場合はOSにさらに要求することができます。返されるメモリは物理的なものでも、仮想的なものでもかまいません(この場合、物理的なRAMは実際にアクセスされるまでメモリを保存するために割り当てられない場合があります)。少なくともプラットフォーム固有のAPIを使用しないでください。これは、メモリハードウェアとカーネルがそれを隠そうとするためです。

2。この「記憶」とは何ですか?配列内のスペース?または、他の何か?

例1では、配列内のスペースのようなものです。返されるアドレスは、物理RAMのアドレス可能なチャンクを識別します。ここでも、RAMアドレスは必ずしもフラットまたは連続ではありません。一部のアドレスはROMまたはI/Oポート用に予約されている場合があります。

例2では、​​より仮想的なもの、つまりプロセスのアドレス空間へのインデックスです。これは、基になる仮想メモリの詳細をプロセスから隠すために使用される抽象化です。このアドレスにアクセスすると、メモリハードウェアが実際のRAMに直接アクセスするか、仮想メモリサブシステムにいくつかのRAMを提供するように要求する必要がある場合があります。

3。この「メモリ」が割り当てられるとどうなりますか?

一般に、要求されたバイト数を格納するために使用できるポインターが返されます。どちらの場合でも、malloc演算子またはnew演算子は、プロセスのアドレス空間のどの部分が使用され、どの部分が空いているかを追跡するハウスキーピングを行います。

4。メモリが解放されるとどうなりますか?

再び一般的に、freeまたはdeleteはいくつかのハウスキーピングを行い、メモリの再割り当てに利用できることを認識します。

また、次のC++行でmallocが何をするのかを誰かが答えてくれれば、本当に助かります。

_char* x; 
x = (char*) malloc (8);
_

NULL(必要な8バイトが見つからなかった場合)またはNULL以外の値のポインターを返します。

このNULL以外の値についてあなたが便利に言えることは、それだけです:

  • これらの8バイトのそれぞれにアクセスすることは合法(かつ安全)です_x[0]..x[7]_、
  • _x[-1]_または_x[8]_にアクセスすること、または実際にany _x[i]_にアクセスすることは違法です(未定義の動作)_0 <= i <= 7_
  • compareの_x, x+1, ..., x+8_のいずれかが有効です(ただし、dereferenceの最後はできません)
  • プラットフォーム/ハードウェア/メモリ内のデータを保存できる場所に制限がある場合は、xがそれらを満たします
7
Useless

メモリを割り当てるとは、オペレーティングシステムにメモリを要求することを意味します。 RAMが必要なときだけ、「スペース」を要求するのはプログラム自体です。たとえば、配列を使用したいがそのサイズがわからない場合プログラムを実行すると、次の2つのことができます:-宣言とarray [x] xを任意に指定して、xを指定します(例:100)。しかし、プログラムが20要素の配列を必要とする場合はどうでしょうか。 。-次に、プログラムはxの正しいサイズを知っているときにx要素の配列をmallocできます。メモリ内のプログラムは4つのセグメントに分割されます。-stack(関数の呼び出しに必要)-code(bibary実行可能コード)-data (グローバル変数/データ)-ヒープ、このセグメントで割り当てられたメモリを見つけます。割り当てられたメモリがもう必要ないと判断したら、オペレーティングシステムに返します。

10個の整数の配列を割り当てたい場合は、次のようにします。

int * array =(int *)malloc(sizeof(int)* 10)

そして、あなたはfree(array)でOSにそれを返します

3
user2204592