C言語の動的メモリ割り当て:malloc()、calloc()関数
C言語の動的メモリ割り当てを学ぶ前に、理解しておきましょう。
C言語のメモリ管理の仕組み
基本的なデータ型を使用して変数を宣言すると、C言語のコンパイラはスタックと呼ばれるメモリのプールに変数用のメモリ領域を自動的に確保します。
たとえば、float変数は、宣言時に通常4バイト(プラットフォームによって異なります)を取ります。 以下の例のように、sizeof演算子を使ってこの情報を確認することができます。
#include <stdio.h>int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}
出力は次のようになります。
The size of float is 4 bytes
また、指定されたサイズの配列は、メモリの連続したブロックに割り当てられ、各ブロックは1つの要素のサイズを持っています。
#include <stdio.h>int main() { float arr;printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}
結果は以下のようになります。
The size of the float array with 10 element is 40
これまで学んだように、基本データ型や配列を宣言すると、自動的にメモリが管理されます。 しかし、C言語には、プログラムを実行するまで(ランタイム)配列の大きさが決まらないプログラムを実現するためのメモリの割り当て方法があります。 この処理を “動的メモリ割り当て “と呼びます。
このチュートリアルでは、以下のことを学びます。
- C言語のメモリ管理はどのように機能するか?
- C言語での動的メモリ割り当て
- C言語のmalloc()関数
- free()関数
- C言語のcalloc()関数
- calloc()とmalloc()の違い。 主な違い
- C の realloc() 関数
- 動的配列
C の動的メモリ割り当て
動的メモリ割り当てとは、プログラミングの必要性に応じてメモリを手動で割り当てたり解放したりすることです。 動的メモリは、ヒープと呼ばれる領域に新たに割り当てられたメモリ空間を指すポインターで管理され、提供されます。
これで、実行時に要素の配列を動的に作成したり破棄したりすることが、問題なくできるようになりました。 要約すると、自動メモリ管理はスタックを使用し、C の動的メモリ割り当てはヒープを使用します。
<stdlib.h>ライブラリには、動的メモリ管理を担当する関数があります。
機能 | 目的 |
malloc() | 要求されたサイズのメモリを割り当て、割り当てられた空間の最初のバイトへのポインタを返します。 |
calloc() | 配列の要素のスペースを割り当てます。 要素を0に初期化し、メモリへのポインタを返します。 |
realloc() | 以前に割り当てられたメモリ空間のサイズを変更するのに使用されます。 |
Free() | 以前に割り当てられたメモリ空間を解放または空にします。 |
上記の関数を応用して説明しましょう
C malloc()関数
C malloc()関数は、メモリの割り当てを意味します。 メモリのブロックを動的に確保するための関数です。 指定されたサイズのメモリ空間を確保し、そのメモリ位置を指すNULLポインターを返します。 返されるポインタは、通常、void型です。 つまり、C言語のmalloc()関数を任意のポインタに割り当てることができるということです。
malloc()関数の構文を示します。
ptr = (cast_type *) malloc (byte_size);
ここでは、
- ptrはcast_typeのポインタです。
- Cのmalloc()関数はbyte_sizeの割り当てられたメモリへのポインタを返します。
malloc()の例。
Example: ptr = (int *) malloc (50)
このステートメントが正常に実行されると、50バイトのメモリ空間が確保されます。 確保された空間の1バイト目のアドレスはint型のポインタptrに割り当てられます。
別の例を考えてみましょう。
#include <stdlib.h>int main(){int *ptr;ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */ if (ptr != NULL) { *(ptr + 5) = 480; /* assign 480 to sixth integer */ printf("Value of the 6th integer is %d",*(ptr + 5)); }}
出力です。
Value of the 6th integer is 480
- *ptrの宣言が後で別のデータ型にタイプキャストされたときにコードがより強固になるように、sizeof(int)の代わりにsizeof(*ptr)が使用されたことに注意してください。
- メモリが十分でない場合、割り当てに失敗することがあります。 この場合は、NULL ポインタを返します。 そのため、NULL ポインタをチェックするコードを含める必要があります。
- 割り当てられたメモリは連続しており、配列として扱うことができることを覚えておいてください。 割り当てられたメモリは連続しており、配列として扱うことができます。
Malloc()関数は、構造体のような複雑なデータ型だけでなく、文字データ型でも使用することができます。
free()関数
変数用のメモリは、コンパイル時に自動的に解放されます。 動的なメモリ割り当てでは、明示的にメモリを解放しなければなりません。 そうしないと、メモリ不足のエラーが発生することがあります。
C言語ではメモリの解放・割り当てを行うためにfree()関数が呼ばれます。プログラム内でメモリを解放することで、後で使用できるメモリを増やすことができます。
例を挙げます。
#include <stdio.h>int main() {int* ptr = malloc(10 * sizeof(*ptr));if (ptr != NULL){ *(ptr + 2) = 50; printf("Value of the 2nd integer is %d",*(ptr + 2));}free(ptr);}
出力
Value of the 2nd integer is 50
Cのcalloc()関数
Cのcalloc()関数は、contiguous allocationの略です。 この関数は、複数のメモリブロックを割り当てるために使用されます。 これは動的なメモリ割り当て関数で、配列や構造体などの複雑なデータ構造にメモリを割り当てるために使用されます。
Malloc()関数は単一のメモリ空間のブロックを割り当てるために使用されますが、Cのcalloc()は複数のメモリ空間のブロックを割り当てるために使用されます。 calloc()関数によって割り当てられた各ブロックは、同じサイズです。
calloc()関数の構文を示します。
ptr = (cast_type *) calloc (n, size);
- 上記のステートメントは、同じサイズのn個のメモリブロックを割り当てるために使用されます。
- メモリ空間が割り当てられた後、すべてのバイトがゼロに初期化されます。
- 割り当てられたメモリ空間の最初のバイトにあるポインタが返されます。
メモリ不足など、メモリ領域の割り当てにエラーがあった場合は、NULLポインタが返されます。
calloc()の例を示します。
以下のプログラムは、算術級数の和を計算するものです。
#include <stdio.h> int main() { int i, * ptr, sum = 0; ptr = calloc(10, sizeof(int)); if (ptr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Building and calculating the sequence sum of the first 10 terms \ n "); for (i = 0; i < 10; ++i) { * (ptr + i) = i; sum += * (ptr + i); } printf("Sum = %d", sum); free(ptr); return 0; }
結果です。
Building and calculating the sequence sum of the first 10 termsSum = 45
calloc() vs. malloc(): 主な違い
C言語におけるmalloc()とcalloc()の主な違いは次のとおりです。
calloc()関数は一般的にmalloc()関数よりも適しており、効率的です。 どちらもメモリ空間を確保するための関数ですが、calloc()は一度に複数のブロックを確保することができます。 毎回、メモリブロックを要求する必要はありません。 calloc()関数は、より大きなメモリ空間を必要とする複雑なデータ構造で使用されます。
C言語のcalloc()で割り当てられたメモリブロックは常にゼロに初期化されますが、C言語のmalloc()関数では常にガベージ値が格納されます。
Cのrealloc()関数
Cのrealloc()関数を使うと、すでに割り当てられたメモリにさらにメモリサイズを追加することができます。 C言語のrealloc()は、メモリの再割り当てを意味します。
realloc()は、以前に割り当てられたメモリのサイズを小さくするためにも使用できます。
realloc()関数の構文を示します。
ptr = realloc (ptr,newsize);
上記のステートメントは、変数newsizeに指定されたサイズの新しいメモリ空間を割り当てます。 この関数を実行すると、メモリブロックの最初のバイトへのポインタが返されます。 新しいサイズは、以前のメモリより大きくても小さくても構いません。 新たに割り当てられたブロックが前のメモリブロックと同じ場所を指すかどうかはわかりません。 この関数は、以前のデータをすべて新しい領域にコピーします。 これにより、データの安全性が確保されます。
realloc()の例を示します。
#include <stdio.h>int main () { char *ptr; ptr = (char *) malloc(10); strcpy(ptr, "Programming"); printf(" %s, Address = %u\n", ptr, ptr); ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size strcat(ptr, " In 'C'"); printf(" %s, Address = %u\n", ptr, ptr); free(ptr); return 0;}
C言語のrealloc()が失敗した場合は、必ずnullポインタを返し、前のデータも解放されます。
C言語の動的配列
C言語の動的配列は、必要に応じて要素の数を増やすことができます。 Cの動的配列は、コンピュータサイエンスのアルゴリズムで広く使われています。
以下のプログラムでは、C言語で動的配列を作成し、サイズを変更しています。
#include <stdio.h> int main() { int * arr_dynamic = NULL; int elements = 2, i; arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks for (i = 0; i < elements; i++) arr_dynamic = i; for (i = 0; i < elements; i++) printf("arr_dynamic=%d\n", i, arr_dynamic); elements = 4; arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements printf("After realloc\n"); for (i = 2; i < elements; i++) arr_dynamic = i; for (i = 0; i < elements; i++) printf("arr_dynamic=%d\n", i, arr_dynamic); free(arr_dynamic); }
画面に表示されたC言語の動的配列プログラムの結果です。
arr_dynamic=0arr_dynamic=1After reallocarr_dynamic=0arr_dynamic=1arr_dynamic=2arr_dynamic=3
概要
- ヒープに必要に応じてメモリブロックを作成することで、メモリを動的に管理することができます
- Cの動的メモリ割り当てでは、実行時にメモリを割り当てます。
- 動的メモリ割り当てでは、サイズが柔軟で、プログラム内でいつでも変更可能な文字列や配列を操作することができます。
- 特定の構造がどのくらいのメモリを占有するか見当がつかない場合に必要です。
- CのMalloc()は動的なメモリ割り当て関数で、特定のサイズのメモリブロックをガベージ値に初期化してメモリを割り当てることを意味します
- CのCalloc()は連続したメモリ割り当て関数で、0に初期化された複数のメモリブロックを一度に割り当てることを意味します
- CのRealloc()は指定されたサイズに従ってメモリを再割り当てするために使用されます。
- Free()関数は、動的に割り当てられたメモリをクリアするために使用されます。