【C言語】「動的メモリ」「malloc関数」←なにこれ?
動的メモリやmalloc関数。この二つを始めてみたとき僕はよくわかりませんでした。大きな壁にぶつかったような感覚でした。
そしてこれは僕に限った話ではないと思います。動的メモリやmalloc関数の扱い方は、C言語を学ぶ過程で、多くの人が挫折する内容の一つだと思います。このことからこの記事を書こうと思いました。
①”動的”メモリとは?
動的メモリを語る際に触れておきたいメモリが一つあります。「静的メモリ」です。静的メモリは動的メモリの比較対象としてよく出てきます。
静的メモリとは
静的メモリはプログラムの実行を開始する前にあらかじめ確保されているメモリの事を指します。
int num; //<-int型の変数一つ分のメモリを確保している。
皆さんは上のようにint型の変数を宣言する文章をよく書くと思います。実はこのように書くことで、みなさんは静的メモリを確保しているのです。
int a[5]; //<-int型の変数5個分のメモリを確保している。
配列を宣言するときは上のように書きます。長さ5のint型配列を宣言していますよね。
これも、int型の変数5個分の静的メモリを確保しているということになります。
このようにプログラムを実行する前(コードを書いている段階)で確保されたメモリを静的メモリと呼びます。
動的メモリとは
察しの良い方は既にお気づきかもしれません。
動的メモリとはプログラムの実行を開始した後に確保されるメモリの事を指します。動的メモリは、静的メモリのようなメモリの確保の仕方はしません。「int 変数名」といった記述はしないということです。
静的メモリは、実行中に確保するメモリ数を変更することはできません。取得した後に「予想していたよりも取得メモリ数が多すぎた!」と気づいてももう遅いのです。
それに対し、動的メモリは実行中にメモリ数を確保できるので、必要な時に必要なだけメモリを確保できます。プログラムを書いている際に必要なメモリ数が分からなくても大丈夫なのです。これにより、メモリの不足や、メモリの節約が可能となります。
では動的メモリはどのようにすれば確保できるのでしょうか。
その答えが、「malloc関数を使用する」です。
②malloc関数でメモリを動的に確保してみる。
まず先に、動的メモリをmalloc関数で取得している様子を見てもらいましょう。
int型の変数1つ分のメモリを取得する。
//実際は include<stdlib.h> する必要があります。
int *pointer;
pointer = (int*)malloc(sizeof(int));
int型のポインタ(今回はpointer)が、int型のメモリ一つ分を指している構造になります。
どのように記述すれば取得できるのかを確認しましょう。
記述方法
その1:型に合わせてキャストする。
malloc関数はint型のみならず、char型など様々な型のメモリを取得できます。そのため、どの型でも取得できるように、malloc関数はあらかじめvoid*型で定義されています。欲しい型を、キャストによって指定しましょう。
自分で取得したい型がint型である場合、「(int*)」をmalloc関数の前に記述し、型をvoid*型からint*型に変換します。(このような型の変換の方法をキャストといいます。)
その2:取得するサイズのメモリのサイズを書く。
欲しいメモリのサイズをmalloc関数の引数リストに記述しましょう。malloc()←このかっこの中が引数のリストです。
今回はint型のメモリ一つ分が欲しいので、「sizeof(int)」と記述します。sizeof(型名)で型のサイズが分かります。
では、int型のメモリ二つ分が欲しいときはどうすればよいでしょうか。
int型のメモリ二つ分が欲しいので、「sizeof(int)*2」と記述します。
int *pointer;
pointer = (int*)malloc(sizeof(int)*2); //int型二つ分のサイズを記述する。
③メモリの確保はどのようにして行われているのか?<発展>
最後にmalloc関数によって行われるメモリ確保を視覚的に見てみましょう。そのためにはまずアドレスについて理解する必要があります。
アドレスとは住所です。どの場所にデータが存在するかを示しています。
変数のアドレスは、変数名の前に「&」を付けることで確認できます。
#include<stdio.h>
void main(void){
int num;
printf("変数numのアドレス(住所)は、%pです。\n, &num);
}
実行結果は以下のようになります。(アドレスの値は環境によって変わる場合があります。)
変数numのアドレス(住所)は、000000FC1E78F564です。
これはつまり、「変数numはメモリ中の '000000FC1E78F564' という住所に住んでいる」ということです。
malloc関数は、「確保したメモリのアドレスを返す」関数です。
//実際は include<stdlib.h> する必要があります。
int *pointer;
pointer = (int*)malloc(sizeof(int));
最初に記述した、int型一つ分のメモリを確保するコードは、確保したint型メモリのアドレスを、ポインタに指させているのです。ポインタは、代入されたアドレスを指します。
確保したメモリをポインタに指させることで、動的確保したメモリにアクセスすることが出来ます。値の格納、出力などが可能になるのです。
#include<stdio.h>
#include<stdlib.h> //malloc関数の使用に必要
void main(void){
int *pointer; //①ポインタ変数を定義する。(int*型の'静的'メモリを確保しています。)
pointer = (int*)malloc(sizeof(int)); //②int型メモリを動的確保,そして③確保したメモリのアドレスをpointerに代入している。
*pointer = 5;
printf("%d",*pointer);
}
出力
5 //pointerに指させたメモリに格納された値を出力
動的確保したメモリに値を格納するイメージも示しておきます。(上のコードの*pointer = 5の部分)
ただメモリを動的確保「(*int)malloc(sizeof(int));」を書いただけではいけません。確保したメモリのアドレスをポインタに指させる「pointer=(int*)malloc(sizeof(int));」ことで、メモリへのデータの格納や出力が可能となるのです。
最後に
malloc関数を理解し使いこなせるようになると、プログラミングが一層楽しくなると思います。ポインタによるメモリ操作はC言語の醍醐味です。malloc関数を理解することはこれらの醍醐味を知ることと同じです。
最初は難しく感じると思いますが、一度理解してしまえばこっちのものです。みなさんの理解の助けとなれば嬉しいです。