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;
}