• ベストアンサー

【VC++6.0(MFC)】メモリの静的、動的割り当ての意味

いつも大変お世話になっています。 VC++初心者です。 大変初歩的な事なのですが、 理解できていないので質問をさせて頂きたいと思います。 new、deleteはメモリを動的に割り当て、開放しるようですが、 この動的とはどういう意味なのでしょうか。 動的があるのなら、静的にメモリ割り当てというものも あるのでしょうか。 (静的、動的を区別することによって得られる 恩恵とは何なのでしょうか。) 具体的に例を挙げていただけると幸いです。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.5

>> 「確保される場所が違う」、これをもう少し厳密に言うと >> 「確保されるメモリの場所が違う」という事でしょうか。 >> (例えば、 >> int i;みたいな変数は、Aという範囲のメモリを使いなさい、 >> new を使った変数は、Bという範囲のメモリを使いなさい、 >> というイメージでしょうか。 >> 稚拙な表現ですみません。) 参照URLにも記載されていますが、【int a】で確保した場合、 関数内のauto変数ならスタック領域、static変数やグローバル変数ならば 静的領域に確保されます。 【new】や【malloc】などで確保した場合はヒープ領域に確保されます。 >> >(可変長のCSVファイルを読み込みや、双方向リストなどのデータ構造を作成したい場合) >> >いきなりデカイローカル変数等などを用意して使いまわしても良いと思いますが、実用的ではありません。 >> この内容を拝見して「何故、実用的でないのか」と疑問に思いました。 >> (確かに、あまり良くないな、感覚的には思うのですが、 >>  論理的には説明できないのです。) >> つまり、私の挙げた例を使うと、メモリ範囲Bがあるのに、 >> メモリ範囲Aの部分を大量に消費してしまうから、 >> 実用的でないということでしょうか。 まず、関数内で宣言されるauto変数は、大概の処理系ではスタック領域に確保されるため、 コンパイルオプション等にもよりますが、それほど大きな領域を確保できませんし、 大きな領域を確保出来る環境を前提に作ってしまうと、移植性に乏しくなってしまいます。 newやmallocでメモリを動的に確保出来る利点は以下の通りと私個人では判断しております。 (1)配列では面倒なデータの削除や追加が可能なデータ構造を実現できる。  通常の配列では、途中のデータを追加・削除しようとすると、データのコピー等が発生するなど  処理が重たくなってしまいます。  そこで、リスト構造などデータの追加・削除が容易な仕組みを実現することが必要となり、  自己参照構造体やメモリの動的確保で実現を行います。 (2)例えば、メモリ領域が最大100M確保可能な環境があるとします。  データaとデータbがあり、それぞれ可変長(0~100M)だが、  データaとデータbは足して100Mを超えることは無いものとします。  上記の場合、データa・データbのそれぞれの最大メモリ使用量は100Mのため  足して200Mの領域を最初に確保したいと思いますが、環境が最大100Mまでしか  確保できないため、出来ません。  そこで動的メモリ確保となってくる訳です。 特にオブジェクト指向でプログラムを組むとなると、各タイミングで各種オブジェクトの 生成・消滅が発生します。 それを前提にある程度のオブジェクト数を最初に用意しておく事も可能ですが、コンストラクタや デストラクタを活かすコードを書こうとした場合、new・delete演算子で生成・消滅させた 方が良いと思います。 私はwindows畑ではありませんが、microsoftのMFCなどもウィンドウやボタンを生成する毎に それに対応するCButtonクラス等を継承したクラスを生成していたはずです。 以上

yuki7091
質問者

補足

ご回答、ありがとうございます。 理解できました。(多分、、、) あとは、本当に理解できたか、プログラミングしてみます。 具体的、及び、丁寧な説明、ありがとうございました。

その他の回答 (4)

回答No.4

文字通り動的にメモリを割り当てるという意味です。 つまりプログラム実行中にメモリ領域を確保する操作のことです。 乱暴な説明ですが、プログラムが使用するメモリ領域はコンパイル時 に確定すると考えていただいて差し支えないと思います。 そういう意味ではコンパイラが割り当てるメモリ領域を静的割り当て と呼べるかもしれません。(ただし、C/C++では「静的」という用語は 少し違う意味で使われますので、ここではこれ以上言及しないことに します。) 動的割り当てがどのような場合に使われるかというと、お粗末な例で すが、サイズが予測できないファイルの内容を配列に読み込みたい場合 に、GetFileSize関数などでファイルサイズを取得した後、p = new char[ファイルサイズ]; で適切なメモリ領域を新たに確保します。 蛇足ですが動的に確保したメモリ領域は、用済みになった後deleteで 解放してやらないとプログラム終了までメモリに残ります。いわゆる、 メモリリークという現象ですが、確保するばかりで解放しないといずれ マシンのメモリリソースを食いつぶしてランタイムエラーを引き起こし ます。これはデバッグが難しい割と大きなトラブルです。なのでJava等 ではガーベッジコレクション等の仕組みが用意されています。 さらに蛇足ですが、オブジェクト指向プログラミングでは良くnewが 使われます。これも動的割り当てなのですが、クラスをインスタンス化 する操作であり少し意味合いが異なります。

yuki7091
質問者

補足

ご回答、ありがとうございます。 >つまりプログラム実行中にメモリ領域を確保する操作のことです。 だから、サイズが多き変数、大きさが分からない変数は、動的メモリ確保 (私の例ではBの範囲のメモリ、正確にはヒープ領域に確保)するんですね。 (そうしないと、スタック領域がいっぱいいっぱいになってしまうから?) >プログラムが使用するメモリ領域はコンパイル時 >に確定すると考えていただいて差し支えないと思います。 普通に使用しているメモリ範囲 (私の例ではAの範囲のメモリ、正確にはスタック領域のメモリ) が、コンパイル時に決定されていることは知りませんでした。 (こういう事をきちんと理解すること、ってとても大事な気がしてきました。) 2つ蛇足(?)のアドバイス、ありがとうございます。 私の理解を深める、及び、省みることができました。

回答No.3

参照URLを参照の事。 何か勘違いされているようですが、strcpyはメモリを確保する命令ではありません。 指定されたアドレスに文字列をコピーするだけです。 #1さんのソースコードで配列でもnewでも256バイト確保されます。 ただ、確保される場所が違うだけです。 >>p[5]~p[255]が無駄ということなのでしょうか。 どちらも使わない限り無駄となります。 deleteは確保した時が配列であれば、 delete []p です。 メモリを動的に確保する理由としては可変長のデータに対応するためです。 (可変長のCSVファイルを読み込みや、双方向リストなどのデータ構造を作成した場合) いきなりデカイローカル変数等などを用意して使いまわしても良いと思いますが、実用的ではありません。

参考URL:
http://www9.plala.or.jp/sgwr-t/c/sec12.html
yuki7091
質問者

補足

ご回答、ありがとうございます。 「確保される場所が違う」、これをもう少し厳密に言うと 「確保されるメモリの場所が違う」という事でしょうか。 (例えば、 int i;みたいな変数は、Aという範囲のメモリを使いなさい、 new を使った変数は、Bという範囲のメモリを使いなさい、 というイメージでしょうか。 稚拙な表現ですみません。) >(可変長のCSVファイルを読み込みや、双方向リストなどのデータ構造を作成した場合) >いきなりデカイローカル変数等などを用意して使いまわしても良いと思いますが、実用的ではありません。 この内容を拝見して「何故、実用的でないのか」と疑問に思いました。 (確かに、あまり良くないな、感覚的には思うのですが、  論理的には説明できないのです。) つまり、私の挙げた例を使うと、メモリ範囲Bがあるのに、 メモリ範囲Aの部分を大量に消費してしまうから、 実用的でないということでしょうか。 しかし、その変数たちが関数内などで局所的に宣言されて いるだけならば、関数を抜けた後はその変数は開放されるので、 コーディングの仕方次第では、動的メモリ確保なんてことを しなくても良いのではないでしょうか。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.2

>1回目のstrcpyでは5バイト確保され、 >2回目のstrcpyでは9バイト確保されるということでしょうか。 newの時点で256バイト確保されます。 strcpy()の実行は関係ありません。 静的確保…というかローカル変数の場合、たいていの処理系でスタック領域から取られることが多いです。 が、このスタック領域はたいていの環境においてサイズが有限です。 # VC系の場合、リンカオプションで指定できデフォルトで1Mだったかと。 スタックが1Mの状態で、コールされた関数内でローカル変数を1M以上確保すると…たいていの場合は落ちます。 # スタックオーバーフローになります。 ちなみにstatic変数だったり、グローバル変数の場合はまた別の領域が割り当てられます。 ということで、大きめなサイズのものは動的確保することでスタック領域とは別のヒープ領域から確保しますし。 確保できない場合は関数が失敗したり、ポインタにNULLが返されたり、例外が投げられたり…で自分でエラー処理することも可能です。 # もっとも、ヒープ領域からのメモリがとれない状況ではたいていどうにもならない…と思われますが。 動的確保の他の必要性では… 実行時までサイズが確定できない場合というのもあります。 ファイルをオンメモリで読み込む場合、コーディング時にサイズが確定できないかも知れません。 255バイトで用意して、読み込もうとしているファイルが1Mだったら? じゃあ、1M用意しておいて読み込もうとしたファイルが1.5Mだったら? ならば2M用意しておいて、読み込もうとしたファイルが10Mあったら? という具合に。 ファイルサイズを得る方法がありますので、読み込む前にファイルサイズ分のメモリを確保すれば無駄がありません。 # もっとも、サイズが大きくなったりすると読み込めるだけのメモリ確保が必ずしも成功するとは限らなくなってきますが。

yuki7091
質問者

補足

ご回答、ありがとうございます。 何となく、理解できました。 つまり、動的と静的のデータを保存する場所が違うから、 大きなデータとか、大きさが分からないデータは newする(動的メモリ確保する)、という認識で宜しいでしょうか。 間違っているようならば、ご指摘頂けると幸いです。

  • phoenix343
  • ベストアンサー率15% (296/1946)
回答No.1

new、deleteがプログラム中にあるなら それは動的といえますね。 char *p = new char[256]; strcpy(p, "hoge"); delete p; 逆に単に変数を宣言しているだけなら それは静的といえます。 char p[256]={0}; strcpy(p, "hoge"); 実は静的に確保できるサイズがアプリごとに決まって(リンカのオプションなどで指定する)いて、静的な確保だけだと、効率のよい処理が出来ません。 なので動的に必要なだけ確保して、必要なくなったら解放するという方法がよくとられます。 …と単純に考えてますが 何かもっと別の理由があるなら識者が答えてくれるでしょう 笑

yuki7091
質問者

補足

早速のご回答、ありがとうございます。 #1さんの例ですと、newで動的にメモリを確保した場合、 p[0]には、'h' p[1]には、'o' p[2]には、'g' p[3]には、'e' p[4]には、'null' の5バイトのみが確保され、 変数宣言のみ例の場合、 p[0]には、'h' p[1]には、'o' p[2]には、'g' p[3]には、'e' p[4]には、'null' p[5]には、'' ... p[255]には、'' であり、p[5]~p[255]が無駄ということなのでしょうか。 上記が正しいすると char *p = new char[256]; strcpy(p, "hoge"); strcpy(p, "hogehoge"); delete p; とした場合、 1回目のstrcpyでは5バイト確保され、 2回目のstrcpyでは9バイト確保されるということでしょうか。

関連するQ&A