本文へスキップ
BecomeCoder

C++コース · 第6章 ポインタとメモリ · レッスン27

スタックとヒープ ― メモリの使い分け(読み物)

ローカル実施

導入

ここまでで「変数メモリに置かれ、住所を持つ」ことを学びました。最後に、そのメモリが実は2つの領域に大きく分かれて使われていることを紹介します。スタックヒープです。ポインタが本当に力を発揮するのは、この「ヒープ」を扱うときです。

このレッスンは概念の理解が目的の読み物です。動的確保(new / delete)はブラウザ内の学習用インタプリタでは扱わないため、手元の本物のコンパイラ(Visual Studio や g++/clang)で試すことを想定しています。

説明

プログラムが動くとき、メモリはおおよそ次のように使い分けられます。

flowchart TB
  subgraph RAM["プログラムが使うメモリの全体像"]
    direction TB
    code["コード領域:プログラムの命令そのもの"]
    global["静的領域:グローバル変数など(ずっと居座る)"]
    heap["ヒープ:自分で確保・解放する広い領域(new / delete)"]
    stack["スタック:関数呼び出しと局所変数の一時置き場(自動)"]
  end
  stack -->|"呼び出しで伸び、戻ると縮む"| stack
  heap -->|"必要なときに好きなだけ借りる"| heap

スタック (stack) は、関数呼び出しのたびに自動で使われる領域です。関数に入ると、その関数のローカル変数(int x など)がスタックに積まれ、関数を抜けると自動でまとめて片付けられます。速くて手間いらずですが、「関数を抜けると消える」「大きさは前もって決まっている」という制約があります。第4章で学んだローカル変数は、すべてこのスタック上にあります。

flowchart TB
  m["main() の枠<br/>ローカル変数"] --> f["func() を呼ぶと<br/>上に積まれる"] --> g["さらに呼ぶと<br/>また積まれる"]
  g -.->|"関数を抜けると上から順に片付く"| m

ヒープ (heap) は、プログラマが「今これだけ必要」と明示して借りる、大きくて自由な領域です。C++ では new で借り、delete で返します。借りたメモリの場所は住所(ポインタ)で受け取ります。だから、ヒープを扱うにはポインタが欠かせません。

// ※この例は手元の本物のコンパイラで実行してください
#include <iostream>
using namespace std;

int main() {
    int *p = new int;   // ヒープから int 1個ぶんを借り、その住所を p に受け取る
    *p = 42;            // 借りた場所に値を書く
    cout << *p << endl; // 42
    delete p;           // 借りたメモリを返す(返し忘れ=メモリリーク)
    return 0;
}

スタックとヒープの違いを整理します。

スタックヒープ
確保・解放自動(関数の出入りで)手動(new / delete
寿命関数を抜けると消えるdelete するまで残る
速さとても速いやや遅い
大きさ小さめ・固定的大きく取れる・可変
使いどころ一時的なローカル変数実行中に大きさが決まるデータ

なぜヒープが必要なのでしょうか。「実行してみないと必要な個数が分からない」データ(利用者が入力した件数ぶんの配列など)は、スタックの固定サイズでは足りません。そこで、実行中に必要なだけヒープから借りるのです。ただし借りたら必ず返す(delete)のがルールで、返し忘れると使わないメモリが増え続ける メモリリーク になります。

flowchart LR
  A["new で借りる"] --> B["ポインタで使う<br/>*p = ..."] --> C["delete で返す"]
  B -.->|"返し忘れると"| D["メモリリーク<br/>(無駄が増え続ける)"]

まとめ

  • メモリは大きくスタック(自動・一時的)とヒープ(手動・自由)に分かれる。
  • ローカル変数はスタック、new で借りるのはヒープ。
  • ヒープの場所はポインタで受け取って使う――これがポインタの最も重要な役割のひとつ。
  • 借りたら返す(delete)。返し忘れはメモリリーク。

現代の C++ では、delete の返し忘れを防ぐために std::vector(次章)やスマートポインタといった安全な道具が用意されています。まずは「メモリには住所があり、ポインタでそれを扱う」という土台をつかめれば十分です。ここで学んだ考え方は、他の言語(Java・C#・Python など)の「参照」や「ガベージコレクション」を理解するときにも必ず役立ちます。