C言語ダイナミックメモリ割り当て関数の実現


Cでメモリ空間を開拓するには二つの方法があります。
1.静的にメモリを開く:例えば:

int a;int b[10];
 このようなメモリ空間の特徴は
開発されたメモリは、スタックにおいて開発された固定サイズであり、aは4バイト、配列bは40バイトであり、配列は申明時にその長さを指定しなければなりません。大域配列であれば、メモリはコンパイル時に割り当てられます。局所変数配列であれば、動作時にスタックにメモリを静的に割り当てます。大域配列であろうと、局所配列であろうと、それらは配列の大きさが確定的であり、コードの中で書き込みが完了しているという特徴があります。もし私達がプログラムを実行する時に一つの配列の大きさを確定したいなら、前の二つのメモリをスタックに割り当てる方法は明らかにだめです。例を挙げます。

int n;
scanf("%d", &n);
int a[n];
このように編集するとコンパイル中にエラーが発生します。コンパイラは[]に定数式を示すべきです。Cで配列を定義する時に使用できるのは以下のような種類があります。

#define N 10
enum NUM{
	M=10
};
int a1[N];
int a2[10];
int a3[M];
注意したいのは、C中const int n=10;n配列長として配列を定義することはできませんが、C++中であれば大丈夫です。 
しかし、私たちは開発空間の需要については、この限りではないことが多いです。最も一般的な定義配列の大きさはプログラムが実行されている時に知っています。静的な開発はもう無力です。もちろん、静的な開発もあります。ダイナミックな開発もあります。次に、ダイナミックなメモリ空間の開発を見に来ました。
2.メモリを動的に開く :
Cにおける動的開発空間は三つの関数を使用する必要がある。malloc(), calloc(), realloc() 、これらの3つの関数は、ヒープに申請されたメモリ空間である。
ヒープの中で申請するメモリの空間はスタックの中で記憶する局部変数のようにはできなくて、関数が呼び出し終わったら自動的にメモリを釈放して、私達が手動で釈放する必要があります。free()関数が完成します。
これらの関数の特徴、使い方、違い、連絡を見てみましょう。
1.mallo()void * malloc(size_t size)1)malloc()関数は、連続的に利用可能なメモリ空間をスタックに申請します。
2)申請が成功したら、このメモリ空間を指すポインタを返します。失敗したら、NULLに戻ります。 だから、私たちはmalloc関数でダイナミックメモリを開発した後、必ず関数の戻り値がNULLかどうかを判断します。
3)戻り値のタイプはvoid*型で、malloc関数は連続して開発されたsizeバイトがどのタイプのデータを格納しているかは分かりませんので、自分で決定する必要があります。方法は、mallo()の前に強制的に回転して、私達の必要なタイプに転化します。
4)sizeが0の場合、この挙動は未定義であり、未知のエラーが発生します。コンパイラに依存します。
具体的にはどう使いますか?例を挙げてください。

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....       
}
このとき、配列p[n]を作成したのに相当します。このnの値は、通常の配列を定義するように定数でなければならず、プログラムを実行する時に得られたもの、またはユーザーが入力したものを必要としません。
 2.free()(int*)malloc(sizeof(int)*n).スタックの中で申請したメモリ空間はスタックに格納されている局部変数のようにはなりません。関数が呼び出されたら自動的にメモリを解放します。手動で釈放しないと、プログラムが実行されてから解放されます。このようにメモリが漏れてしまう恐れがあります。つまり、ヒープの中のこのメモリのデータはもう使われなくなりましたが、ずっとこの空間を占めています。(一般的には茅坑を占めているので、糞をしないということです。)だから、私達が申請したダイナミックメモリはもう使わない時、必ず適時に釈放します。
1)ptrがダイナミックメモリ割り当て関数を使って割り当てられたメモリ空間を指していない場合、未定義の動作を引き起こす。
2)ptrが空のポインタである場合、この関数は何も実行されません。
3)この関数はptr自体の値を変更しませんので、同じ(現在は無効)の場所(メモリ)を指します。
4)free()関数の後にptrを空にする必要がある、すなわちptr= NULLptrを空にしないと、後のプログラムがptrを通じて無効になったメモリやリサイクルされたメモリにアクセスできます。プログラムの頑丈さを保証するために、ptr=を書きます。 NULL; 
注意:free()は重複して一つのメモリを釈放してはいけません。

free(ptr);
free(ptr);
間違っています。すでに釈放されたメモリは繰り返しリリースできません。メモリエラーが発生します。
free()具体的な使い方、例を挙げます。

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....       
}
//     ,          
free(p);
p = NULL;
 3.caloc()void free(void* ptr)malloc()関数との違いは、caloc()関数がアドレスを返す前に、アプリケーションのメモリ空間の各バイトを0に初期化することにある。 .
1)caloc()関数機能は、num個のサイズ(バイト長)をsizeとして動的に割り当てるメモリ空間である。
2)申請が成功したら、このメモリ空間を指すポインタを返します。失敗したら、NULLに戻ります。 したがって、caloc関数でダイナミックメモリを開発した後、関数の戻り値がNULLかどうかを必ず判断します。
3)戻り値のタイプはvoid*型で、caloc()関数はnum個のsizeサイズのメモリ空間を割り当てますが、どのタイプのデータが記憶されているかは分かりません。 ,したがって、私たちは自分で決定する必要があります。方法はcaloc()の前に強制的に回転し、必要なタイプに転化します。
4)sizeとnumのいずれかが0である場合、この挙動は未定義であり、未知のエラーが発生し、コンパイラに依存します。
したがって、どのようにして私たちはアプリケーションのメモリ空間の内容を初期化する必要があります。
たとえば:

4.realloc()void * calloc(size_t num,size_t size)realloc()関数は、ダイナミックメモリ管理をより柔軟にします。プログラム実行中にメモリサイズを動的に割り当てます。  割り当てが大きすぎると、スペースが無駄になります。小さすぎると、まだ足りない状況があるかもしれません。メモリを合理的に利用するために、メモリのサイズを柔軟に調整します。realloc関数は、ダイナミックなメモリサイズの調整を行うことができます。
1)ptrは調整が必要なメモリアドレスです。
2)sizeは調整に必要なサイズ(バイト数)です。
3)調整に成功した場合、戻り値がサイズ調整後のメモリの開始位置(つまり調整後のメモリを指すポインタ)となり、失敗したら(メモリがないと割り当てられない場合は、普通は出現しない)NULLに戻りますので、戻り値を空にします。
4)ptrが空のポインタであれば、malloc()関数と同じように作用します。
注意:realloc()関数はメモリ空間を広げる時に2つの状況があります。
1)ptrが指すメモリの後に十分なメモリ空間があります。

2)ptrが指すメモリの後には十分な空間がないので、図のように拡張します。 

第二の場合、 新しいメモリ空間を申請すれば、ptrが指すメモリの内容を新しいメモリ空間にコピーします。 ptrが指すメモリはリリースされ、新しいメモリアドレスに戻ります。成功しないとptr 指し示すメモリはリリースされません。関数はNULLに戻ります。
5.まとめ
1)malloc()とcaloc()関数の使い方は同じですが、唯一の違いはcaloc()は申請したメモリのバイトごとに0に初期化されます。
2)malloc()、 caloc()、realloc()申請のメモリが使われなくなった場合、必ずfree()を使ってリリースしてください。メモリが漏れます。
3)p=realloc(ptr,size)関数の戻り値が空でない場合、メモリを解放する時にfree(ptr)を書く必要がなく、free(p)を書くだけです。 
ここでC言語ダイナミックメモリ割り当て関数の実現に関する記事を紹介します。C言語に関するダイナミックメモリ割り当て関数の内容については、以前の文章を検索したり、下記の関連記事を見たりしてください。これからもよろしくお願いします。