今回はC言語の malloc
関数について解説していきたいと思います。
Contents
malloc
関数の定義
では早速 malloc
関数の定義を紹介します。
#include <stdlib.h>
void *malloc(size_t);
malloc
関数は動的にメモリを確保する関数です。
成功時には確保したメモリのアドレスが、失敗時には NULL
が返却されます。
引数には確保したいサイズをバイト単位で指定します。
また、定義されているファイルは stdlib.h
なので、stdlib.h
を include
してから使用してください。
うん。そうだね。
いきなり「動的確保」って言われても意味が分からないよね…
malloc
関数をもっと詳しく理解するために、動的確保、さらにはメモリについても一緒に解説していくよ!malloc
関数をより深く理解するためには、メモリやメモリの確保について理解することが重要です。ここからは、このメモリやメモリの確保についてまず説明し、続いて malloc
関数の使い方やメリットデメリット等について解説していきたいと思います。
C言語プログラムとメモリ
前述の通り、malloc
関数についてしっかり理解するには、メモリについて理解するのが手っ取り早いです。
なので、まずはメモリについて解説していきます。
スポンサーリンク
メモリ
メモリとはデータを記憶するハードウェアです。
パソコンのスペック表などを見てもこのメモリのサイズが必ず明記されていると思います。
最近だと10GB を超えるサイズのメモリが内蔵されているパソコンも多く発売されていますね!
10GBのメモリでは、1バイト分のデータを 1024 * 1024 * 1024 * 10 個分記憶することができます。
メモリを図示すると ↓ のようになります。下側の数字はアドレスで、アドレスは各バイトの位置を示す数値となります。
どんなプログラミング言語で作成したプログラムにおいても、動作するにはデータの記憶が必要であり、その時にこのメモリを利用して動作しています。
例えば電卓アプリではユーザーが入力した数値をメモリに記憶して計算を実行したり、ウェブブラウザではダウンロードしたデータをメモリに記憶して動画を再生したりしています。
メモリの割り当て
パソコン上では非常に多くのアプリが同時に動作しており、各アプリがメモリを利用しています(アプリだけでなくオペレーティングシステムもメモリを利用しています)。
この時、異なるアプリ同士が同じメモリを使用してしまうと、データが壊れてしまう可能性があります。
このようなことが起こらないように、オペレーティングシステム(OS)はアプリ毎に使用中のメモリ領域や使用されていないメモリ領域を管理しています。
アプリ起動時には、オペレーティングシステムが他のアプリに使用されていないメモリ領域からアプリが起動するのに必要なサイズ分のメモリを確保し、そのメモリをアプリに割り当ててから起動させるようになっています。
そしてアプリはその確保されたメモリを利用して動作します。ポイントは、自身のアプリ用に確保されたメモリしか使用してはいけない点です。
他のアプリ用に確保されたメモリを使用すると、前述したようにデータ破壊などが行われてしまいます。
なので、他のアプリ用に確保されたメモリのデータを使用されそうになったらエラーが出るようになっています。これがプログラム実行時に良く見かける Segmentation fault です。
こんな感じの制御が行われることで、アプリ同士で同じメモリを使用してデータを壊さないようになっています。
こういう話を聞くと、メモリサイズが大きいパソコンの方が同時にアプリを動作させる数が多くなることも論理的に理解できると思います。
C言語プログラムが扱えるメモリ
ここまで一般的なアプリの例で解説してきましたが、これらは私たちがC言語でプログラミングするプログラムにおいても同じです。
つまり、私たちが作成したプログラムも、起動時にオペレーティングシステムによってプログラムが起動するために必要なメモリが確保され、そのメモリを利用して動作します。
この時に確保されるメモリのサイズはプログラムの内容によって異なります。例えば下記のような情報に基づいてサイズが決まります。
- プログラム自体のサイズ
- グローバル変数や
static
変数のサイズ- 型や個数によって決まる
- スタックサイズ
プログラムが使用するデータという観点で考えると「グローバル変数や static
変数のサイズ」が確保されるメモリのサイズに直接関係あることになります。
これらの変数がプログラムで使用できるように、変数宣言されている変数のサイズ分のメモリを確保し、プログラム起動時にこれらの変数がその確保されたメモリに配置されることになります。
ローカル変数(動的変数)はスタックに格納されるデータであり、プログラム実行時に確保されるメモリサイズには直接関係ありません
ただしローカル変数がスタックサイズに収まりきらない場合はスタックオーバーフローエラーが発生してしまいます
スタックのサイズはスレッドごとに設定できたりします
ただし、オペレーティングシステムは宣言された変数分のメモリしか確保してくれませんので、変数宣言していない分のメモリは使用できないことになります。
前述のとおり、プログラムはそのプログラム用に確保されたメモリしか使用してはいけないのです。
つまり、ソースコードを書く時にはあらかじめプログラムで必要な数・サイズを決定して、その分の変数を宣言してやる必要があることになります。
この時に特に困るのが配列です。配列では変数宣言時にサイズ(もしくは配列に格納するデータそのもの)を指定する必要があります。
int array1[100];
char array2[] = "aiueo";
ですが、ソースコードを書いている時点ではサイズを決めるのが難しい場合があります。
例えばファイルから読み込んだ文字列を配列に格納するプログラムなどは、読み込むファイルが変わるとファイルのサイズが変わるので必要になるサイズも毎回変わってしまうことになります。
こんな感じでサイズが一つに定まらないケースはプログラミングしてると非常に多くの場面で出くわします。
スポンサーリンク
メモリの動的確保
こんな時に便利なのが「メモリの動的確保」です。動的確保とは、プログラム起動時に確保されたメモリ以外のメモリを後から(プログラム起動した後から)追加で確保する手段になります。
つまり、ソースコードで変数宣言を行なって確保したメモリ “以外の” メモリを後から追加して使用することができます。
例えば前述のファイル読み込みの例であれば、プログラム起動後にプログラムにファイルサイズを調べさせ、そのファイルサイズ分のメモリを後から追加で確保することができます。
ですので、ソースコード記述時に必要なメモリのサイズや個数が定まらないような場合に、プログラム起動後に必要になった分のメモリだけを後から追加することができます。
メモリの動的確保に対し、プログラム起動時に決まったサイズ分メモリを確保することはメモリの静的確保といいます。
例えばグローバル変数や static
変数を宣言することは「メモリを静的確保すること」と捉えることができます。
malloc
関数とは
そして、この動的確保を行う関数の1つが malloc
関数です。
malloc
関数
malloc
関数の定義を下記に再掲しておきます。
#include <stdlib.h>
void *malloc(size_t);
malloc
関数の引数には追加で確保したいメモリのサイズをバイト単位で指定します。引数の型は size_t
となります。
malloc
関数を実行することで、引数に指定したサイズ分のメモリをオペレーティングシステムに要求することができます。
スポンサーリンク
malloc
関数の成功時の動作
そしてオペレーティングシステムが、このプログラム用にメモリを確保できた場合は、確保したメモリの先頭アドレスを戻り値として返却します。返却値の型は void*
です。
この時、先頭アドレスから引数で指定したサイズ分のメモリだけが、プログラム動作用に追加されたことになります。つまり、先頭アドレスから指定したサイズ分のメモリをプログラムが自由に利用することが可能です。
他のプログラムからは基本的にこのメモリは使用されません。ですので、他のプログラムからの影響を受けることなく使用することができます。
ただし、この範囲を超えてメモリを利用しようとするとエラー(Segmentation fault)が発生することがあります。
すぐエラーが発生せずに、後から(解放時など)発生する場合もあります
エラーが発生しない場合もあります
エラーが発生しない場合でも、使用することが許可されていないメモリを利用することは禁止されており、データを壊してしまう可能性があります
確保したメモリ以外にはアクセスしないようにしましょう
また、この返却されるアドレスの型は void*
型になります。
下記ページでも解説していますが、void*
型の変数はただのアドレスを格納するだけのものであり、アドレスの指す先のデータを参照したり、この型の変数に演算することは禁じられています。

一般的なポインタ同様に使用するためには、 malloc
関数の返却値をキャストする必要があります。
int *addr;
addr = (int*)malloc(4);
よく malloc
関数の使用例で上記のようにキャストを行っているものが見かけると思いますが、理由は前述の通り、malloc
関数の戻り値の型である void*
型ではアドレスの指す先のデータを参照したり、この型の変数に演算することが禁じられているためです。
また、malloc
関数の戻り値は必ず受け取るようにしましょう。malloc
関数で確保したメモリはアドレスを指定して使用する必要があります。このアドレスを忘れてしまうと、そのメモリを利用することはできません。
malloc
関数の失敗時の動作
malloc
関数が失敗した時には NULL
が返却されます。ですので、メモリの動的確保に成功したかどうかは、下記のように malloc
関数の戻り値を NULL
かどうかを調べることで判断することができます。
int *addr;
addr = (int*)malloc(4);
if (addr == NULL) {
/* エラー処理 */
}
malloc
関数が失敗したときは、メモリが確保されなかったということです。ですので、確保しようとしたメモリは使用できません。
例えば malloc
関数に失敗したのに下記のように malloc
関数の戻り値のアドレスにアクセスしようとすると、使用が許可されていないメモリにアクセスすることになります。
int *addr;
addr = (int*)malloc(4);
if (addr == NULL) {
*addr = 1024;
}
どんな時に malloc
関数が NULL
を返却するかというと、メモリが確保できなかった場合です。
例えば malloc
関数で要求したメモリサイズが大きすぎる場合などはエラーになることがあると思います。
ただし、オペレーティングシステムがかなり賢いようで、実際のパソコンに搭載しているメモリサイズを超えて要求してもエラーにならない場合もあるようです。
malloc
関数の引数
前述の通り、malloc
関数の引数には追加で確保したいメモリのサイズをバイト単位で指定します。引数の型は size_t
となります。
#include <stdlib.h>
void *malloc(size_t);
バイト単位というところが1つのポイントであり、注意点でもあります。ここについて解説しておきます。
例えば配列であれば、下記のように変数宣言すれば int
型の変数8個分のメモリが確保されることになります。
int array[8];
ご存知の通り型にはサイズが定義されており、一般的に int
型のサイズは4バイトです。
ですので、バイト単位で考えると32バイト分のメモリが確保されることになります。
一方で、下記のように malloc
関数を実行したとしても、引数で指定しているサイズが8バイトですので、8バイト分のメモリしか確保されません。キャストで int*
に型変換していますが、アドレスの型が変換されるだけですので確保するサイズに影響はないです。
int *addr;
addr = (int*)malloc(8);
8バイトですので、これを int
型のデータとして扱うことを考えると2つ分のメモリしか確保できていないことになります。
要は、malloc
関数の引数は型のサイズを考慮して指定する必要があるということです。int
型の変数8個分のメモリを確保したいのであれば、4 * 8
を指定する必要があります。
でも型のサイズをわざわざ指定するのは面倒です。型のサイズを全部記憶している方も少ないと思いますし、型のサイズは環境によって変わったりします。
この型のサイズを取得するのに便利なのが sizeof
演算子です。sizeof
演算子の引数に型名を指定すれば、指定した型のサイズを取得することができます。
int
型の変数8個分のメモリを確保する場合、malloc
関数の引数には sizeof
演算子を利用して下記のように指定すれば良いです。
int *addr;
addr = (int*)malloc(sizeof(int) * 8);
スポンサーリンク
malloc
関数で確保したメモリの使い方
続いて malloc
関数で確保したメモリの使い方を解説していきます。
前述の通り、malloc
関数の戻り値は確保したメモリの先頭アドレスになります。そして、その先頭アドレスから、malloc
関数に引数で指定したサイズ分のメモリを使用することができます。
で、確保したメモリの使い方は基本的にポインタと同じになります。配列を指すポインタと考えるとより分かりやすいと思います。
ということで、まずは配列を指すポインタの使い方をおさらいしておきましょう。
配列を指すポインタ
下記はポインタ addr
に配列 array
の先頭アドレスを指させ、addr
から array
のデータにアクセスするプログラムになります。
#include <stdio.h>
/* intデータ4つ分のメモリ */
int array[4];
int main(void){
int i;
int x;
int *addr;
/* 配列の先頭を指す */
addr = array;
for (i = 0; i < 4; i++) {
/* 要素を指定してアクセス */
addr[i] = i * 1024;
}
for (i = 0; i < 4; i++) {
/* "*"演算子を利用してアクセス */
x = *addr;
printf("%d : %d\n", i, x);
/* アドレス値を加算 */
addr++;
}
return 0;
}
配列 array
は int array[4];
と変数宣言していますので、プログラム起動時に連続する int
型のデータ4つ分のメモリが確保されることになります。
配列には要素を指定することで(array[1]
など)、各要素のデータにアクセスすることができます。これと同様に、配列を指すポインタも同様に要素を指定することで配列の各要素のデータにアクセスすることができます。
/* 要素を指定してアクセス */
addr[i] = i * 1024;
また、ポインタは *
演算子を用いることで、ポインタの指す先(ポインタに格納されているアドレス)のデータにアクセスすることもできます。
/* "*"演算子を利用してアクセス */
x = *addr;
さらに、ポインタ変数に対して加算や減算を行うことで、ポインタに格納されているアドレスを増減させることができます。要はポインタの指す先を変更することができます。
/* アドレス値を加算 */
addr++;
もっと正確に言うと、ポインタへの加減算によるアドレスの増減量やアクセスするデータのサイズはポインタの型によって異なります。
この辺りは下記ページで解説していますので、こちらも是非読んでみてください。

malloc
で確保したメモリを指すポインタ
続いて malloc
関数で確保したメモリを指すポインタについて見ていきましょう。
先ほどの配列を指すポインタと同じように扱うことができます。
これは、確保の仕方は異なるものの、どちらも結局は同じメモリだからです。
下記はポインタ addr
に malloc
関数で確保したメモリの先頭アドレスを指させ、addr
からそのメモリのデータにアクセスするプログラムになります。
#include <stdlib.h>
#include <stdio.h>
int main(void){
int i;
int x;
int *addr;
/* intデータ4つ分のメモリを確保 */
addr = (int*)malloc(sizeof(int) * 4);
if (addr == NULL) {
printf("malloc error\n");
return -1;
}
for (i = 0; i < 4; i++) {
/* 要素を指定してアクセス */
addr[i] = i * 1024;
}
for (i = 0; i < 4; i++) {
/* "*"演算子を利用してアクセス */
x = *addr;
printf("%d : %d\n", i, x);
/* アドレス値を加算 */
addr++;
}
free(addr);
return 0;
}
メモリを確保する方法は異なるものの、データへのアクセスの仕方やアドレスへの加算や減算を行う処理は配列を指すポインタの場合と全く同じです。
こんな感じで、malloc
関数で確保したメモリも、いつものポインタと同じように扱うことが可能です。
メモリが不要になったら free
関数で解放
ただし、静的に確保した(グローバル変数等の宣言により確保した)メモリとは異なり、動的確保したメモリは不要になったら free
関数で解放を行う必要があります。
free
関数の定義は下記のようになります。
#include <stdlib.h>
void free(void*);
引数には動的確保したメモリの先頭アドレスを指定します。
解放というと具体的なイメージが付かないかもしれませんが、要はオペレーティングシステムに「このアドレスのメモリは不要だからお返しします」と宣言することです。
この宣言を受けてオペレーティングシステムは、そのアドレスのメモリが不要になったことを認識し、空きメモリとして扱います。これにより、次に他のアプリやプログラムからそのメモリが使用可能になります(オペレーティングシステムからそのメモリを確保して使用)。
malloc
関数の使用例
続いては malloc
関数の実際の使用例を見て malloc
関数のイメージを具体化していきましょう!
スポンサーリンク
ファイルを読み込むプログラム
ここまでの例でも挙げてきたファイルを読み込むプログラムのソースコード例は下記になります。
読み込むファイルのサイズ分のメモリを malloc
関数で動的確保しています。
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(void) {
char file_name[] = "test.txt";
FILE *fi;
struct stat stat_data;
size_t file_size;
char *addr;
/* ファイルのサイズを取得 */
if (stat(file_name, &stat_data) != 0) {
printf("statに失敗しました\n");
return -1;
}
file_size = stat_data.st_size;
fi = fopen(file_name, "r");
if (fi == NULL) {
printf("ファイルオープンエラー\n");
return -1;
}
/* file_size分のメモリを動的確保 */
addr = (char*)malloc(sizeof(char) * file_size);
if (addr == NULL) {
printf("mallocに失敗しました\n");
fclose(fi);
return -1;
}
/* ファイルのデータの読み込み */
fread(addr, file_size, 1, fi);
fclose(fi);
/* 動的確保したメモリの解放 */
free(addr);
return 0;
}
ファイルのサイズを取得するのに stat
関数を利用していますが、おそらく Windows 環境では使用できないと思います。Windows ではファイルサイズを取得する関数 GetFileSize
が用意されていますので、そちらの関数を利用してファイルサイズを取得すると良いと思います。
ポインタ変数だけで動作するプログラム
次は単純に配列にデータを格納し、続いて格納したデータを表示するプログラムです。
普通に書くとソースコードは下記のようになります。
#include <stdio.h>
#define SIZE 1024
int data[1024];
int main(void) {
int i;
for (i = 0; i < SIZE; i++) {
data[i] = i;
}
for (i = 0; i < SIZE; i++) {
printf("%d\n", data[i]);
}
return 0;
}
これをポインタ変数だけを利用して記述するとソースコードは下記のようになります。
#include <stdio.h>
#include <stdlib.h>
#define SIZE 1024
int main(void) {
int *i;
int *data;
/* int変数1つ分のメモリを確保 */
i = (int*)malloc(sizeof(int));
if (i == NULL) {
printf("mallocに失敗しました\n");
return -1;
}
/* int変数SIZE分のメモリを確保 */
data = (int*)malloc(sizeof(int) * SIZE);
if (data == NULL) {
printf("mallocに失敗しました\n");
free(i);
return -1;
}
for (*i = 0; *i < SIZE; (*i)++) {
data[*i] = *i;
}
for (*i = 0; *i < SIZE; (*i)++) {
printf("%d\n", data[*i]);
}
/* 動的確保したメモリを解放 */
free(i);
free(data);
return 0;
}
両方で結果は同じになります。この2つのプログラムの1番の違いは、メモリの確保の仕方の違いです。ただし、確保の仕方が違うだけで、同じサイズのメモリを利用しているので、同じ処理を行うプログラムを実現することができています。
こんな感じで malloc
関数を使えばポインタの変数宣言だけでいろんなプログラムを作成することができます。
その他の例
その他にも私のサイトでは malloc
関数を利用する例をたくさん公開しています。例えば下記のページなどでは malloc
関数を利用したソースコードを公開していますので、よろしければこちらも是非読んでみてください。



スポンサーリンク
動的確保のメリット・デメリット
次はメモリの動的確保(malloc
)のメリットとデメリットについて解説していきたいと思います。基本的にグローバル変数や static
変数の宣言で行われる静的なメモリ確保(プログラム起動時に決まったサイズ分メモリを確保すること)と比較してのメリット・デメリットになります。
メモリの動的確保のメリット
ではまずはメリットを見ていきましょう。
ソースコード記述時にメモリ使用量を決める必要がない
ソースコード記述時にメモリ使用量を決める必要がないところがメリットの1つ目です。
前述したように、ファイル読み込み時にファイルのサイズが分からない時など、使用したいメモリ量がソースコード記述時には決まらないときに便利です。
後からプログラム動作時に実際に必要なサイズが確定してからメモリを追加で確保することができますからね。
ソースコード記述時に無理にサイズを決めなくても良いので、ちょっとした検証用や実験を行う時は特に動的確保は向いていると思います。
ポインタについての知識が深まる
また、malloc
関数の戻り値はアドレスですので、使用するためにはポインタの知識が必須です。なので malloc
関数を使うことで自然とポインタの知識が深まり、ポインタにもすぐ慣れることができます。
ポインタと聞くと苦手意識を持つ方もおられるかもしれませんし、むしろデメリットと捉える方もいると思います。
ただC言語とポインタは切っても切れない関係ですし、そのポインタに慣れることができるのは、私としてはメリットだと考えています。
メモリの動的確保のデメリット
次はデメリットです。
確保したメモリの情報の管理が大変
動的確保したメモリは、ポインタ変数で先頭アドレスを覚えておかないと後から解放することができません。解放できないとメモリリークになります。
また、確保したメモリのサイズを超えてアクセスすると他のアプリのメモリを壊してしまう(もしくはエラーになる)可能性があります。メモリのサイズを超えてアクセスしないように制御する必要があります。
なので、動的確保する場合は、メモリの先頭アドレスやメモリのサイズは変数で保持しておき、それらを参照しながらプログラミングする必要があります。
例えばグローバル変数の配列であれば解放をする必要はありませんので、先頭アドレスを覚えておく必要もありません。
またサイズに関しても配列のサイズはソースコード記述時にもう決まっていますので、そのサイズを使用してプログラミングしてやれば良いだけです。わざわざ変数でサイズを保持しておく必然性はありません。
こういったアドレスやサイズを管理できるように変数を用意したり解放等を行う必要があるので、静的確保の場合に比べてソースコードが複雑になります。
タイミングによってメモリ確保の成功失敗が異なる
ここが一番のデメリットだと思います。
タイミングによってメモリ確保の結果が異なる可能性があります。
例えば、あるタイミングでは他のアプリがあまりメモリを使用していないので malloc
関数に成功しましたが、他のタイミングだと他のアプリがメモリを大量に使用していて malloc
関数に失敗してしまうようなことが起こり得ます。
malloc
関数を大量に使用するようなプログラムだと、自身のプログラムでメモリを大量に確保しているために、あるタイミングでは malloc
関数に失敗してしまうようなケースもあります。
特にプログラムが複雑な場合、タイミング依存で発生するエラーの原因調査や対策は大変です。
一方で、静的なメモリ確保のみを行う場合、プログラム起動時にメモリが全て確保されることになりますので、プログラム動作中にメモリが足りなくなるようなことはありません。
プログラムさえ動作できればタイミング依存でメモリが足りなくてエラーになるようなことがないので、タイミング依存のエラー要因を減らすことができます。
要は、動的確保を行うことでプログラムの動作が複雑になってしまいます。ここがデメリットです。
スポンサーリンク
動的確保したくない場合は…
デメリットを理解すると、動的確保したくないと言う方も出てくるかもしれません。
静的確保だけでプログラミングすることってできないの?
タイミング依存を嫌う人は多いよね
実は静的確保だけでプログラミングする方法はあるよ!
次はこの方法について解説していこう
ソースコード記述時にサイズが確定しない場合でも、静的確保だけでメモリを確保するにはどうすれば良いでしょうか?最後にこの点について解説していきたいと思います。
仕様でサイズの上限を決める
ソースコード記述時にサイズが確定しない場合は、「プログラムの仕様でサイズの上限を決めてしまう」ことでメモリの静的確保だけでプログラミングすることができます。
例えばファイルを読み込むようなプログラムの場合、「このプログラムでは1KBを超えるファイルは読み込めません」というように、読み込むファイルの上限を仕様として設定してしまえば良いです。
そして、ファイルのデータを格納する配列もサイズを1KBに設定して宣言してやれば良いです。
char array[1024];
ファイルサイズが1KBを超えるデータはプログラムの仕様として受け付けませんので、これ以上メモリを確保する必要はなく、動的確保は必要ありません。
うーん、プログラムの動作に制限かけるってこと?
なんかイマイチだなぁ…
いや、動的確保にしても制限はあるんだ
メモリも無限にあるわけじゃないからね
タイミングによってその制限が変化していつエラーになるか分からないプログラムよりも、その制限を1つに定めてそれをユーザーに明示してあげた方がよっぽど便利だと思うけどね
メモリも有限ですので、動的確保をしたからといってどんなサイズのデータも扱えると言うわけではないです。
結局は扱えるデータサイズに上限があります。厄介なのは動的確保の場合はこの上限がタイミングによって変わるところです。
ここで紹介している方法は、その上限を自分自身で一定サイズに決めてしまう方法であり、別に邪道な方法ではありません。
上限が一定サイズなのでユーザーにとってもわかりやすいプログラムを提供することができます。
ただし、この仕様はユーザーに明示し、この仕様を超える場合は、その旨をユーザーに伝えるようにしましょう。でないと、なぜプログラムが上手く動作してくれないか?どうすればプログラムをうまく動作させられるのかがユーザーに伝わりませんからね。
例えば下記はファイルを読み込むプログラムで、「読み込めるファイルのサイズの上限は1KB」という仕様になっています。
#include <stdio.h>
#include <sys/stat.h>
/* 読み込むファイルの上限サイズ */
#define MAX_SIZE 1024
/* 読み込んだファイルのデータを格納する配列 */
char array[MAX_SIZE];
int main(void) {
char file_name[] = "test.txt";
FILE *fi;
struct stat stat_data;
size_t file_size;
/* ファイルのサイズを取得 */
if (stat(file_name, &stat_data) != 0) {
printf("statに失敗しました\n");
return -1;
}
file_size = stat_data.st_size;
if (file_size > MAX_SIZE) {
printf("1KBを超えるファイルは読み込めません\n");
return -1;
}
fi = fopen(file_name, "r");
if (fi == NULL) {
printf("ファイルオープンエラー\n");
return -1;
}
fread(array, file_size, 1, fi);
fclose(fi);
return 0;
}
このプログラムでは仕様を超えるサイズのファイルを読み込もうとすると「1KBを超えるファイルは読み込めません」とエラーメッセージを表示するようにしています。
こんな感じで仕様を決めることで、ソースコード記述時にサイズが確定しない場合でも、静的確保のみでプログラムを実現することができます。
まとめ
このページでは malloc
関数について、特にメモリやメモリの確保を絡めて解説しました。
メモリについても一緒に学ぶことで、動的確保・malloc
関数についての理解も深められたのではないかと思います!
プログラムが動作するためにはメモリが必要です。
そして、プログラム起動時ではなく、プログラム起動後に追加でメモリを確保するのが動的確保です。
動的確保したメモリは基本的に「配列を指しているポインタ」と同様に扱えます。
動的確保したメモリは解放する(free
関数を実行する)必要がある点には注意しましょう!
静的確保だけでプログラムを作成するか、動的確保も行ってプログラムを作成するかは、メリットやデメリットを理解した上で使い分けると良いと思います!