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;
}
💡 ヒント:
new と delete は必ずペア —— 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
newとdeleteを必ずペアで書く- 解放後は
nullptrを代入 - スマートポインタ
📖 まとめ
- スタック:コンパイル時確保、自動解放、高速
- ヒープ:実行時確保、手動解放(
delete)、低速 newでメモリ確保、deleteでメモリ解放new[]で配列確保、delete[]で配列解放- メモリリーク:
delete忘れ、最悪クラッシュ - ダングリングポインタ:解放済みメモリを指す、危険
📝 練習問題
-
初級(難易度 ⭐):
newでdoubleを1つ確保し、値を3.14に設定して出力。 -
中級(難易度 ⭐⭐): ユーザー入力で
nを読み取り、newで動的にn個のint配列を確保、n個の整数を入力し、最大値を出力。 -
上級(難易度 ⭐⭐⭐): 動的メモリで「動的配列クラス」を実装(クラス、または構造体を使う):
- メンバー:
int* data(ヒープ上の配列)、int size(現在の要素数)、int capacity(容量) push_back関数を実装:size == capacityなら、メモリを再確保(newで新しい配列、コピー、古い配列を解放)- テスト:100個の要素を追加
- メンバー:
7. 🚀 次のステップ
動的メモリを理解したら、次は ポインタと参照の総合練習(レッスン27) —— 総合的な実例で、理解を定着させよう!