C言語では、メモリの動的確保を行うことができます。動的確保は、必要なメモリ領域が実行時に変化する場合や、大量のメモリ領域が必要な場合に便利です。
ここでは、メモリを動的に確保する方法と、その注意点について解説します。
動的メモリ確保と解放を行う関数
C言語では、動的なメモリの確保と解放を行うために、標準ライブラリで専用の関数が提供されています。
#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
使用するには、stdlib.h
をインクルードする必要があります。
メモリの動的確保
メモリを動的に確保するにはmalloc()
関数を使用します。
void *malloc(size_t size);
malloc()
は、引数に必要なメモリ領域のサイズをバイト単位で指定します。戻り値として、割り当てられたメモリ領域の先頭アドレスが返されます。メモリ割り当てに失敗した場合は、NULL
が返されます。
例えば、10個の要素を持つint
型の配列を割り当てる場合は、以下のように書きます。
int *ptr;
ptr = (int *)malloc(sizeof(int) * 10);
この例では、int
型のサイズをsizeof
演算子で取得し、10倍した値をmalloc()
に渡しています。これにより、int
型のサイズ×10のメモリ領域が割り当てられ、その先頭アドレスがポインタ変数であるptr
に代入されます。
割り当てられたメモリ領域は、ポインタ変数を介して、通常の配列と同じように操作することができます。
ptr[0] = 100; // 0番目の要素に100を代入
ptr[1] = 200; // 1番目の要素に200を代入
確保したメモリの開放
動的に確保したメモリ領域は、使用後に必ず解放する必要があります。解放しないとメモリリークが発生し、システム全体のメモリを使い尽くしてしまう可能性があります。
解放するためには、free()
関数を使用します。
void free(void *ptr);
free()
は、引数に解放するメモリ領域の先頭アドレスを指定します。
free(ptr);
これにより、動的に確保されたメモリ領域が解放されます。
初期化されたメモリの確保
calloc()
関数を使用することで、値が0で初期化されたメモリを確保することができます。
void *calloc(size_t nmemb, size_t size);
malloc()
と引数は異なりますが、基本的な考え方は同じです。
size
に要素のサイズ、nmemb
に要素の数を指定してメモリの確保を行います。戻り値として、割り当てられたメモリ領域の先頭アドレスが返されます。メモリ割り当てに失敗した場合は、NULL
が返されます。
int *ptr;
ptr = (int *)calloc(10, sizeof(int));
この例では、要素のサイズにint
型のサイズを指定し、要素の数を10としています。これにより、int
型のサイズ×10のメモリ領域がptr
に割り当てられます。
また、calloc()
で割り当てられたメモリの値は、全て0で初期化されています。
メモリの再確保
realloc()
関数を使用することで、malloc()
やcalloc()
で確保したメモリのサイズを変更することができます。
void *realloc(void *ptr, size_t size);
realloc()
は、ポインタptr
が示すメモリのサイズをsize
バイトに変更します。戻り値として、再確保されたメモリ領域の先頭アドレスが返されます。メモリの再確保に失敗した場合は、NULL
が返されます。
int *new_ptr;
new_ptr = (int *)realloc(ptr, sizeof(int) * 20);
この例では、malloc()
やcalloc()
で確保したメモリの先頭アドレスを渡し、変更後のサイズとしてint
型のサイズ×20を指定しています。
これにより、int
型のサイズ×20のメモリ領域がnew_ptr
に割り当てられます。
動的メモリ確保の注意点
動的メモリ確保には、以下のような注意点があります。
メモリ確保の結果を確認する
動的メモリ確保は失敗する可能性があります。そのため、戻り値を必ずチェックするようにしましょう。
メモリ確保に失敗した場合、関数は戻り値としてNULL
を返します。NULL
ポインタの参照は未定義の動作を引き起こすため、必ず関数の戻り値を確認する必要があります。
int *ptr;
ptr = (int *)malloc(sizeof(int) * 10);
if (ptr == NULL)
{
/* エラー処理 */
}
この例では、malloc()
を例として説明していますが、calloc()
やrealloc()
でも同様に戻り値の確認が必要です。
特にrealloc()
では、引数として渡すポインタ変数と戻り値を受け取るポインタ変数を、異なる変数にしなければいけないことに注意してください。
int *tmp;
tmp = (int *)realloc(ptr, sizeof(int) * 20);
if (ptr == NULL)
{
/* エラー処理 */
}
else
{
ptr = tmp; /* 成功した場合は上書き */
}
こうすることで、メモリの再確保に失敗した場合でも、元となるptr
がNULL
で上書きされることはありません。
不要となったメモリは解放する
malloc()
関数で割り当てたメモリ領域は、必ずfree()
関数で解放する必要があります。解放しないと、メモリリークが発生し、システム全体のメモリを使い尽くしてしまう可能性があります。
メモリ確保と解放が関数内で1対1となるようにプログラムすることで、目視での確認が容易となり、メモリリークの発生を抑えることができます。
メモリの二重開放をしない
動的に確保したメモリを1度解放した後、再度同じ領域を解放すると未定義の動作となります。
解放されたメモリを再利用している他の部分の動作が不安定になったり、プログラムがクラッシュする可能性があるため、メモリの二重開放に注意する必要があります。
引数にNULL
が指定された場合は、何の動作も行われないことが仕様として定義されているため、メモリを解放した後のポインタ変数にはNULL
を代入することを推奨します。
free(ptr);
ptr = NULL;
コメント