C/C++ で配列などを利用する時に、データをある程度見積もって、それよりも大きいサイズの配列を宣言する。しかし、プログラムを実行している途中で、予想外に大きなデータが生じた時に、配列のサイズを拡大しなければ、すべてのデータを格納できなくなる。この対策として、メモリのサイズを動的に割り当てたり、拡大したりする。C 言語では malloc や free などで、C++ 言語では new や delete などでメモリの管理を行う。
C 言語によるメモリ管理
メモリ領域の確保
C 言語でメモリ領域を動的に確保するときに malloc 関数を利用する。malloc 関数に確保したいメモリのサイズを引数に指定すると、その分のメモリ領域が確保され、そのメモリ領域へのポインタが返される。OS による制限やハードウェアによる制限などで、メモリが確保できない場合も想定される。そのとき、malloc 関数は NULL を返す。
C 言語にはガベージコレクションがないので、malloc 関数で確保したメモリが使わなくなったら、free 関数で解放する必要がある。メモリの解放を行わない場合に、メモリの再利用が行われない。そのためプログラムで、メモリの解放を行わないで次々と mallloc 処理を行ったとき(メモリリーク)、いずれ使用可能なメモリ量を使い果たしてしまい、プログラムが異常終了したり、システムが極端に重くなったりする。
#include <stdlib.h>
#include <stdio.h>
int main() {
int *p = (int *)malloc(sizeof(int)*10);
if (p == NULL) {
printf("Memory cannot be allocated.");
} else {
printf("Memory has been allocated. The address is %p\n", p);
}
free(p);
return 0;
}
メモリ領域の再確保
malloc 関数を利用して確保したメモリ領域を、さらに拡大したい場合は realloc 関数を利用する。realloc 関数の第 1 引数には malloc で確保したメモリ領域へのポインタを指定し、第 2 引数にはメモリ領域のサイズそ指定する。新しいメモリ領域が確保できたとき、新メモリ領域へのポインタが返される。新しいメモリ領域が確保できなかったときは、NULL を返す。realloc 関数は、次のようなプロセスでメモリの再確保を行う。
- 新しく確保するメモリ領域は、旧メモリ領域に比べて小さければ、旧メモリ領域を縮小して、旧メモリ領域へのポインタをそのまま返す。逆に、新しく確保するメモリ領域は、旧領域に比べて大きければ、メモリ上の連続して空いているところで、指定されたサイズの新メモリ領域を確保する。新メモリ領域を確保できた場合は、次に進む。新メモリ領域を確保できなかった場合は、
NULLを返して終わる。 - 旧メモリ領域から新メモリ領域にデータをコピーする。データをそのままコピーするため、旧メモリ領域が大きいほど、コピーに要する時間も長くなる。
- 旧メモリ領域を解放する。
- 新メモリ領域へのポインタを返す。
realloc 関数のメモリ再確保のプロセスのように、メモリの再確保に失敗した場合は、旧メモリ領域は解放されないので、必要に応じて自分で解放する必要がある。次のサンプルコードは、はじめに確保した int 型 10 個分の領域を、20 個分に拡大する例である。
#include <stdlib.h>
#include <stdio.h>
int main() {
int *p = (int *)malloc(sizeof(int) * 10);
if (p == NULL) {
printf("Memory cannot be allocated.");
// free `p`, if required
} else {
printf("Memory has been allocated. The address is %p\n", p);
}
int *q = (int *)realloc(p, sizeof(int) * 20);
if (q == NULL) {
printf("Memory cannot be enlarged.");
} else {
printf("Memory has been enlarged. The new address is %q\n", q);
}
free(q);
return 0;
}
C++ 言語によるメモリ管理
C++ ではメモリの確保は new を利用し、メモリの解放は delete を利用する。delete で解放できるのは new で確保したメモリだけである。malloc や realloc 関数で確保したメモリを delete する場合は定義されていないので、何が起こるのかはわからない。
include <iostream>
include <memory>
// g++ sample.cpp -std=c++0x
int main () {
int* p = nullptr;
try {
p = new int[10];
std::cout << p << std::endl;
} catch (const std::bad_alloc& e) {
std::cout << "cannot be allocated." << std::endl;
}
delete p;
return 0;
}