41

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

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

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

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

4

4 に答える 4

69

メモリモデル

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)ではないポインタである場合、割り当て解除関数は、ポインタによって参照されるストレージの割り当てを解除し、割り当て解除されたストレージの任意の部分を参照するすべてのポインタを無効にします。 。

newdelete

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

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>ヘッダーは、および関数stdlib.hを含むC標準ライブラリのコンテンツを取り込みます。また、C ++標準で定義されている割り当ておよび割り当て解除関数と同様に、メモリの割り当ておよび割り当て解除もC標準で定義されています。(C99§7.20.3.3)の定義は次のとおりです。mallocfreemalloc

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

そしてfree(C99§7.20.3.2)の定義:

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

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


質問への回答

だからあなたの質問に直接答えるには:

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

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

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

    標準に関する限り、メモリは単なるバイトシーケンスです。標準は典型的なコンピュータシステムをモデル化しようとするだけなので、これは意図的に非常に一般的です。ほとんどの場合、これはコンピュータのRAMのモデルと考えることができます。

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

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

  4. メモリの割り当てが解除されると、正確にはどうなりますか?

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

  5. また、誰かがこれらのC++行でmallocが行うことを答えることができれば本当に役に立ちます。

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

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

于 2013-03-24T22:03:24.057 に答える
14

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

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

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

通常、メモリはコンピュータのRAMです。メモリを巨大な「配列」と考えると役立つ場合、それは確かに1つのように動作し、それを1トンのバイト(8ビット値、値によく似ていunsigned charます)と考えます。これは、メモリの下部にある0のインデックスから始まります。ただし、以前と同じように、ここには多数の例外があり、メモリの一部がハードウェアにマップされているか、まったく存在していない可能性があります。

3)この「メモリ」が割り当てられると、正確にはどうなりますか?

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

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

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

5)誰かがこれらのC ++行でmallocが行うことを答えることができれば、それは本当に私を助けます:

本当に大雑把に、そしてそれが関数内にあると仮定します(ちなみに、それはあなたのメモリの割り当てを解除せず、メモリリークを引き起こすので決してこれをしないでください):

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

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

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

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

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ごと、システムごとなどに大きく異なります。

于 2013-03-24T22:07:15.030 に答える
9

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

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

sftrabbitの回答は、この目的の概要を示しています(そして、本当に必要なのはそれだけです)が、役立つ場合に備えて、いくつかの実例を示します。

例1:

十分に古いシングルユーザーコンピューター(または十分に小さい組み込みコンピューター)では、ほとんどの物理RAMがプログラムで直接使用できる場合があります。このシナリオでは、mallocorを呼び出すこと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]実際にアクセスする x[i]ことは違法(未定義の動作)です。0 <= i <= 7
  • いずれかを比較することは合法です(ただし、最後のものを逆参照x, x+1, ..., x+8することはできません)
  • プラットフォーム/ハードウェア/メモリ内のデータを保存できる場所に制限がある場合は、それらをx満たします
于 2013-03-24T22:31:36.163 に答える
4

メモリを割り当てるとは、オペレーティングシステムにメモリを要求することを意味します。これは、必要な場合にのみRAM内の「スペース」を要求するのはプログラム自体であることを意味します。たとえば、配列を使用したいが、プログラムを実行する前にそのサイズがわからない場合は、次の2つのことを実行できます。--declareとarray [x]で、xは任意の長さです。たとえば100です。しかし、プログラムに20個の要素の配列が必要な場合はどうでしょうか。あなたは何のために記憶を無駄にしている。-次に、プログラムは、xの正しいサイズを知っているときに、x要素の配列をmallocできます。メモリ内のプログラムは、次の4つのセグメントに分割されます。-スタック(関数の呼び出しに必要)-コード(バイナリ実行可能コード)-データ(グローバル変数/データ)-ヒープ。このセグメントには、割り当てられたメモリがあります。割り当てられたメモリがもう必要ないと判断した場合は、

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

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

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

于 2013-03-24T21:59:59.103 に答える