C++: 動的メモリ管理

最終更新:2026-08-31

これまで、変数や配列はスタック(Stack)に確保 —— 関数終了とともに自動的に解放。

でもサイズが実行時に決まる場合(ユーザー入力で1000個の要素、コンパイル時には不明)、動的メモリが必要 —— ヒープ(Heap)にメモリを確保。


1. スタック vs ヒープ

(1) 1.1 メモリの種類

特徴 スタック(Stack) ヒープ(Heap)
確保タイミング コンパイル時 実行時
サイズ決定 固定(コンパイル時) 可変(実行時)
解放タイミング 自動(関数終了時) 手動(delete
速度 高速 低速
主な用途 ローカル変数、配列 動的なサイズのデータ

(2) 1.2 確保の種類

種類 プログラムでの意味
静的(コンパイル時) スタック
動的(実行時) ヒープ


2. new と delete

(1) 2.1 new でメモリを確保

構文:

▶ サンプル 2:動的メモリ管理(難易度 ⭐)

TEXT 📖 参照専用
型* ポインタ名 = new 型;

例:

CPP
#include <iostream>

int main() {
    // 動的に1つのintを確保
    int* p = new int; // pはヒープ上のintを指す
    
    *p = 5; // 値を代入
    std::cout << "*p = " << *p << std::endl; // 5
    
    return 0;
}
💡 ヒント: new は確保したメモリのポインタを返す —— ヒープ上のメモリを指す。


(2) 2.2 delete でメモリを解放

構文:

TEXT 📖 参照専用
delete ポインタ名;

例:

CPP
#include <iostream>

int main() {
    int* p = new int;
    *p = 5;
    
    std::cout << "*p = " << *p << std::endl;
    
    delete p; // ✅ ヒープ上のメモリを解放
    p = nullptr; // ✅ 推奨:ヌルポインタに設定
    
    return 0;
}
💡 ヒント: newdelete必ずペア —— new したら必ず delete する。


(3) 2.3 delete 忘れはメモリリーク

エラー例:

CPP
#include <iostream>

void foo() {
    int* p = new int;
    *p = 5;
    // ❌ delete p; を忘れた
} // 関数終了でpは消えるが、intは解放されない(メモリリーク)

int main() {
    for (int i = 0; i < 1000000; i++) {
        foo(); // 4バイトずつリーク
    }
    // 最終的にメモリ不足
    return 0;
}
💡 メモリリークとは: ヒープ上のメモリが解放されず、どこからも参照できなくなる(ポインタが消える)。



3. 動的配列

(1) 3.1 new で配列を確保

構文:

TEXT 📖 参照専用
型* ポインタ名 = new 型[要素数];

例:

CPP
#include <iostream>

int main() {
    int n;
    std::cout << "要素数を入力:";
    std::cin >> n;
    
    // 動的にn個のintを確保
    int* arr = new int[n];
    
    // 値を代入
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    
    // 出力
    for (int i = 0; i < n; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    // 解放(注意:deleteではなく delete[])
    delete[] arr;
    
    return 0;
}
💡 ヒント: 配列は delete[] を使う(delete ではない)!


(2) 3.2 new と delete の対応表

確保 解放
new int delete p;
new int[10] delete[] p;

エラー例:

CPP
int* p = new int[10];
delete p; // ❌ エラー:delete[] p; を使うべき


4. ダングリングポインタ

(1) 4.1 ダングリングポインタとは?

ダングリングポインタは解放済みメモリを指すポインタ。

CPP
#include <iostream>

int main() {
    int* p = new int(5);
    delete p; // 解放
    
    // ❌ 危険:pは今ダングリングポインタ
    std::cout << *p << std::endl; // 未定義動作!出力は変わるかも
    
    return 0;
}
💡 解決策: 解放後は nullptr を代入:

CPP
delete p;
p = nullptr; // ✅ 安全


5. 実践例:動的配列のソート

▶ サンプル 1:動的配列で選択ソート(難易度 ⭐⭐)

CPP
#include <iostream>

// 選択ソート
void selectionSort(int* arr, int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        // 交換
        int temp = arr[i];
        arr[i] = arr[minIdx];
        arr[minIdx] = temp;
    }
}

int main() {
    int n;
    std::cout << "要素数を入力:";
    std::cin >> n;
    
    int* arr = new int[n]; // 動的配列
    
    std::cout << n << "個の整数を入力:" << std::endl;
    for (int i = 0; i < n; i++) {
        std::cin >> arr[i];
    }
    
    selectionSort(arr, n); // ソート
    
    std::cout << "ソート結果:" << std::endl;
    for (int i = 0; i < n; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    delete[] arr; // 解放
    arr = nullptr;
    
    return 0;
}
▶ 試してみよう

出力例:

TEXT 📖 参照専用
要素数を入力:5
5個の整数を入力:
5 3 1 4 2
ソート結果:
1 2 3 4 5


6. よくあるエラー

(1) 6.1 二重 delete

エラー例:

TEXT 📖 参照専用
int* p = new int(5);
delete p;
delete p; // ❌ エラー:二重解放(未定義動作)

解決策: nullptr を代入(delete nullptr は安全):

TEXT 📖 参照専用
delete p;
p = nullptr;

(2) 6.2 delete 忘れ

エラー例:

CPP
int* arr = new int[10];
// ❌ delete[] arr; を忘れた

結果: メモリリーク。


▶ サンプル 3:動的配列のサイズ変更(難易度 ⭐⭐)

CPP
#include <iostream>

int main() {
    int* arr = new int[5];
    int capacity = 5;
    int size = 0;

    // 要素を追加
    for (int i = 0; i < 7; i++) {
        if (size >= capacity) {
            // サイズ変更:大きな配列を新規作成
            int* newArr = new int[capacity * 2];
            for (int j = 0; j < size; j++) {
                newArr[j] = arr[j];
            }
            delete[] arr;
            arr = newArr;
            capacity *= 2;
        }
        arr[size++] = i * 10;
    }

    std::cout << "要素: ";
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    delete[] arr;
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
要素: 0 10 20 30 40 50 60

❓ よくある質問

Q メモリリークを避けるには?
A

  1. newdelete を必ずペアで書く
  2. 解放後は nullptr を代入
  3. スマートポインタ

📖 まとめ

  • スタック:コンパイル時確保、自動解放、高速
  • ヒープ:実行時確保、手動解放(delete)、低速
  • new でメモリ確保、delete でメモリ解放
  • new[] で配列確保、delete[] で配列解放
  • メモリリークdelete 忘れ、最悪クラッシュ
  • ダングリングポインタ:解放済みメモリを指す、危険

📝 練習問題

  1. 初級(難易度 ⭐): newdouble を1つ確保し、値を 3.14 に設定して出力。

  2. 中級(難易度 ⭐⭐): ユーザー入力で n を読み取り、new で動的に n 個の int 配列を確保、n 個の整数を入力し、最大値を出力。

  3. 上級(難易度 ⭐⭐⭐): 動的メモリで「動的配列クラス」を実装(クラス、または構造体を使う):

    • メンバー:int* data(ヒープ上の配列)、int size(現在の要素数)、int capacity(容量)
    • push_back 関数を実装:size == capacity なら、メモリを再確保(new で新しい配列、コピー、古い配列を解放)
    • テスト:100個の要素を追加


7. 🚀 次のステップ

動的メモリを理解したら、次は ポインタと参照の総合練習(レッスン27) —— 総合的な実例で、理解を定着させよう!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%