第14回 実行中にメモリを確保する動的メモリ確保
はじめに
前回は、プログラムで扱うデータを保存するためのファイル操作について学びました。
ファイルを利用することで、プログラム終了後もデータを保存できるようになりました。
今回は、プログラム実行中に必要な大きさのメモリを確保する動的メモリ確保について学びます。
これまで変数や配列を作成するときは、プログラムを書く時点で必要な大きさを決めていました。
例えば、
int score[100];
では、100個の整数を保存できる配列を作成しています。
しかし、この方法には問題があります。
例えば、
- 実際には10人分しか必要ない
- 100人以上になる可能性がある
という場合、あらかじめ適切な大きさを決めることが難しくなります。
そこで、プログラムの実行中に必要な分だけメモリを確保する仕組みを使用します。
これが動的メモリ確保です。
今回の到達目標
- 動的メモリ確保が必要な理由を理解する。
newを使用してメモリを確保できる。deleteを使用してメモリを解放できる。- 動的配列を作成できる。
- メモリ管理の重要性を理解する。
メモリと変数
これまで使用してきた変数は、プログラムを作成した時点でメモリの大きさが決まります。
例えば、
int num = 10;
の場合、整数を保存するためのメモリが自動的に用意されます。
このようなメモリ管理は、C++が自動的に行っています。
しかし、必要なデータ量が実行するまで分からない場合があります。
例えば、
何人分の点数を保存しますか?
というプログラムでは、ユーザーの入力によって必要なメモリの大きさが変わります。
このような場合に動的メモリ確保を使用します。
動的メモリ確保とは
動的メモリ確保とは、
プログラムの実行中に必要なメモリを確保すること
です。
通常の変数では、
プログラム開始
↓
メモリ確保
↓
処理
という流れになります。
一方、動的メモリでは、
プログラム開始
↓
必要になった時
↓
メモリ確保
という流れになります。
newによるメモリ確保
動的にメモリを確保するには、newを使用します。
例:
int* ptr = new int;
このコードでは、
- 整数1つ分のメモリを確保する
- 確保した場所のアドレスを返す
- そのアドレスをポインタに保存する
という処理を行っています。
図で表すと、
ptr
↓
┌────┐
│ │
└────┘
のように、ポインタが新しく作られたメモリを指しています。
動的に確保したメモリを使用する
確保したメモリへアクセスするには、ポインタを使用します。
#include <iostream>
int main()
{
int* ptr = new int;
*ptr = 100;
std::cout << *ptr;
delete ptr;
}
実行結果
100
*ptrを使用することで、確保したメモリへ値を保存したり、値を取得したりできます。
これは第10回で学んだポインタと同じ仕組みです。
deleteによるメモリ解放
動的に確保したメモリは、使用後に解放する必要があります。
解放には、
delete
を使用します。
例:
delete ptr;
deleteを実行すると、確保していたメモリをコンピュータへ返します。
動的メモリ確保では、
確保する
↓
使用する
↓
解放する
という流れが重要です。
メモリリーク
メモリを解放し忘れると、使用されていないメモリが残り続けます。
これをメモリリークといいます。
例えば、
int* ptr = new int;
を実行した後、
delete ptr;
を行わなかった場合、
そのメモリはプログラムが終了するまで使用されたままになります。
小さなプログラムでは問題にならない場合もありますが、大きなプログラムでは大量のメモリを消費する原因になります。
動的配列
動的メモリ確保は、配列にも利用できます。
通常の配列では、
int data[10];
のようにサイズを決める必要があります。
しかし、動的配列を使用すると実行中にサイズを決定できます。
例:
int size;
std::cin >> size;
int* data = new int[size];
この場合、入力された数だけ整数を保存できる配列が作成されます。
例えば、
5
と入力すると、
int* data = new int[5];
と同じ意味になります。
動的配列の解放
配列として確保したメモリを解放するときは、
delete[]
を使用します。
例:
delete[] data;
通常のメモリ解放と違い、配列の場合は[]を付ける必要があります。
動的配列を利用した例
#include <iostream>
int main()
{
int size;
std::cout << "人数を入力してください:";
std::cin >> size;
int* score = new int[size];
for(int i = 0; i < size; i++)
{
std::cout << i + 1 << "人目:";
std::cin >> score[i];
}
for(int i = 0; i < size; i++)
{
std::cout << score[i] << std::endl;
}
delete[] score;
}
このプログラムでは、
- 人数を入力する
- 人数分のメモリを確保する
- 点数を保存する
- 使用後に解放する
という流れになっています。
動的メモリ確保とポインタの関係
ここまでの内容から分かるように、動的メモリ確保とポインタは深く関係しています。
理由は、
new
が返すものが**メモリの場所(アドレス)**だからです。
そのため、
new
で作ったメモリは、
ポインタ
を使って管理します。
注意点
動的メモリ確保では、次の点に注意する必要があります。
1. 解放を忘れない
delete ptr;
を忘れるとメモリリークになります。
2. 解放後のポインタを使わない
解放した後のポインタは、使用してはいけません。
delete ptr;
// 使用しない
3. nullptrを利用する
解放後に、
ptr = nullptr;
としておくと、何も指していない状態を明確にできます。
まとめ
今回は、実行中に必要なメモリを確保する動的メモリ確保について学びました。
- 通常の変数や配列は事前に大きさを決める必要があります。
- 動的メモリ確保では実行中に必要な分だけメモリを確保できます。
newを使用するとメモリを確保できます。deleteを使用すると確保したメモリを解放できます。- 動的配列では
new[]とdelete[]を使用します。 - メモリリークを防ぐため、使用後の解放が重要です。
動的メモリ確保を理解することで、状況に応じて必要な量のメモリを扱えるようになりました。
次回は、プログラムを作成する前段階で処理を制御する前処理プログラムについて学びます。