C++ でスタックとヒープメモリの割り当てを利用する

胡金庫 2023年10月12日
C++ でスタックとヒープメモリの割り当てを利用する

この記事では、C++ でスタックとヒープのメモリ割り当てを利用する方法のいくつかの方法について説明します。

C++ でのスタックメモリとヒープメモリの違い

メモリの概念について説明するときは、最も一般的なユーザープログラムが実行されるシステムの観点から考えるのが最善です。ほとんどのユーザープログラムはオペレーティングシステム環境で実行されます。オペレーティングシステム環境は、ハードウェアリソースを管理し、ユーザープログラムでは処理するには複雑すぎたり非効率的だったりするさまざまなタスクを処理します。そのようなタスクの 1つは、ハードウェアメモリを直接管理することです。したがって、ほとんどすべてのオペレーティングシステムは、ハードウェアメモリと対話するための特別な構造と機能を提供します。オペレーティングシステムが提供するメモリ構造の 2つの一般的な概念は、スタックとヒープです。

スタックは、システムで実行中の各プログラム用に予約されたメモリ領域であり、LIFO 方式で動作します。つまり、プログラムが main 関数の実行を開始すると、後者はスタックフレーム(スタックメモリのサブセット)を取得し、ローカル変数と関数呼び出しのリターンアドレスが自動的に格納されます。main が別の関数を呼び出すと、前のスタックフレームの後に新しいスタックフレームが継続的に作成されます。最新のスタックフレームは、対応する関数のローカルオブジェクトを格納し、それが返されると、これらのメモリは使用されなくなります。

スタックサイズはほとんどのシステムでデフォルトで固定されていますが、ユーザーが特別なニーズを持っている場合はある程度カスタマイズできることに注意してください。スタックメモリのサイズ制限により、小さくてほとんど一時的なオブジェクトに適しています。たとえば、Linux オペレーティングシステムのユーザープログラムのデフォルトのスタックサイズは 8MB です。プログラムが処理する必要のある 1 枚の JPEG 写真よりも小さい場合があるため、ユーザーはこのスペースを慎重に使用する必要があります。次のコードスニペットで宣言された変数はすべてスタックメモリに格納されます。原則として、staticvolatile などの特別な指定子がない場合、すべてのローカル変数はスタックに割り当てられます。

#include <iostream>

using std::cout;
using std::endl;

int main() {
  int var1;
  int var2 = 123;
  int arr1[4] = {1, 2, 3, 4};
  int var3 = var2;

  cout << var1 << endl;
  cout << var2 << endl;
  cout << var3 << endl;

  return EXIT_SUCCESS;
}

出力:

0
123
123

一方、-ヒープ(free store とも呼ばれます)と呼ばれるメモリ領域があり、実行時にプログラマが手動で大きなオブジェクトを格納および割り当てを行うことができます。これらの 2つの機能により、ヒープメモリは本質的に動的になります。これは、コンパイル時やプログラム実行中の任意の時点でヒープメモリのサイズを決定する必要がないためです。プログラムは、特別な関数を呼び出して、オペレーティングシステムに割り当てを要求できます。ヒープメモリは、別の割り当て関数を呼び出してより多くのメモリを要求することに限定されないため、プログラムの観点からは無限に見える場合があることに注意してください。ただし、オペレーティングシステムは、実行中のすべてのプロセスのメモリを管理します。また、使用可能な物理メモリがなくなると、新しい割り当てが拒否される可能性があります。

オペレーティングシステムのメモリシステムは非常に複雑であり、さまざまな OS /ハードウェア固有の概念を理解する必要があるため、このトピックでは、ヒープメモリとスタックメモリについて最低限以上のことを説明します。C++ 言語でのヒープメモリの手動管理は、new/delete 演算子または malloc/free 関数を使用して実行できます。これらの関数は同様に機能し、ユーザーは通常、割り当てるバイト数を指定し、同じ量のメモリが割り当てられたアドレスを返すことに注意してください。その結果、プログラマーは必要に応じて特定のメモリー領域を操作できます。

次のコードサンプルは、ヒープメモリにさまざまなオブジェクトを割り当てるいくつかのケースを示しています。手動メモリ管理の重要な機能の 1つは、割り当てられたメモリ領域が不要になったときにオペレーティングシステムに戻すことです。後者の操作は、対応する割り当てに対応する delete/free 呼び出しを使用して実行されます。プログラムが不要なメモリを解放しない場合、オペレーティングシステムのメモリが不足するリスクがあり、その結果、プログラムが強制終了される可能性があります。ただし、前の問題は実行時間の長いプログラムで発生することがほとんどであり、メモリリークのバグとして特徴付けられていることに注意してください。

#include <iostream>

using std::cout;
using std::endl;

int main() {
  auto var4 = new int;
  cout << var4 << endl;

  int *arr2 = new int[4];
  auto arr3 = new int[4];
  cout << arr2 << endl;
  cout << arr3 << endl;

  delete var4;
  delete[] arr2;
  delete[] arr3;
  return EXIT_SUCCESS;
}
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C++ Memory