【C言語】動的メモリ確保の方法と注意点

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; /* 成功した場合は上書き */
}

こうすることで、メモリの再確保に失敗した場合でも、元となるptrNULLで上書きされることはありません。

不要となったメモリは解放する

malloc()関数で割り当てたメモリ領域は、必ずfree()関数で解放する必要があります。解放しないと、メモリリークが発生し、システム全体のメモリを使い尽くしてしまう可能性があります。

メモリ確保と解放が関数内で1対1となるようにプログラムすることで、目視での確認が容易となり、メモリリークの発生を抑えることができます。

メモリの二重開放をしない

動的に確保したメモリを1度解放した後、再度同じ領域を解放すると未定義の動作となります。

解放されたメモリを再利用している他の部分の動作が不安定になったり、プログラムがクラッシュする可能性があるため、メモリの二重開放に注意する必要があります。

引数にNULLが指定された場合は、何の動作も行われないことが仕様として定義されているため、メモリを解放した後のポインタ変数にはNULLを代入することを推奨します。

free(ptr);
ptr = NULL;
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次