変数が宣言されると、変数のデータを格納するための領域がメモリ上に自動的に確保されます。この仕組みは便利ですが、宣言された変数の型のサイズ分しかメモリ領域は確保されません。もし入力のサイズが不定なプログラムを書こうと思うと、どれだけ変数を用意すればよいのかわからなくて困ることになります。また、ポインタ型の変数が宣言されても、ポインタの指す先のメモリ領域までは確保されないということになります。例えば、以下のようにポインタ型として宣言された「p」に対して、宣言の直後に「*p」への代入を行うことはできません。この例では、そもそも「p」の値が不定なので、ポインタの指す先があるかないか分かりません。
int *p;
*p = 0;
確保されていないメモリ領域にデータを書き込もうとすると、実行時にエラーが発生します。このエラーはコンパイラでは検出できません。Cのプログラムで最もよくあるエラーの一つなので、ポインタの指す先の領域が書き込み可能であるかどうかは常に意識してください。自動で割り当てられるメモリ領域では足りないという場合には、「malloc」という関数を用いて、プログラマがメモリ領域の確保を指示します。
変数が割り当てられていないデータにメモリを使いたい場合、「malloc」(または「calloc」)を使います。使い方は簡単で、「malloc」にサイズを引数として与えると、そのサイズのメモリ領域を確保して先頭のポインタを返してくれます。「malloc」の引数の型は、「size_t」です。「size_t」型では、「1」は「char」型のデータ1つ分を意味します。「char」以外の型のデータの大きさは、sizeof演算子で得ることができます。
char *cp;
int *ip;
cp = malloc(10);
ip = malloc(sizeof(int));
この例では、「malloc」の返す型は「char *」にも「int *」にも見えます。実は、「malloc」の返す型は「void *」です。「void *」型はどのようなポインタ型にもキャスト(型変換)可能です。代入文で左右の型が異なる場合、自動的に右辺の型は左辺の型にキャストされることになっているので、ここでは明示的な型変換は不要です。古いCの仕様では、これらの仕組みがなかったため、明示的にキャストを書いていました。その名残からか「malloc」を常に型情報付きで使用する人もいます。
「malloc」と「calloc」の違いは、引数の数と確保したメモリが0で初期化されるかどうかです。「malloc」で確保したメモリ領域は初期化されません。必要なら、自分で明示的に値を代入してください。また、「malloc」を使うときには、プログラムの先頭に以下のinclude命令を書くのを忘れないでください。
#include <stdlib.h>
「malloc」や「calloc」で確保された領域は連続しているので、ポインタの+演算でアクセス可能です。
cp[0] = 'a'; cp[1] = 'b'; cp[2] = 'c'; cp[3] = '\0';
また、「malloc」はメモリの確保に失敗すると「NULL」を返します。エラー処理をしたいなら、返り値を「NULL」と比較してください。
途中で不必要になったメモリ領域は「free」で解放します。「free」の引数はポインタです。
free(p);
「malloc」で確保したメモリ領域は全て、プログラムが終了するまでに「free」で解放しなければならないというポリシーの人もいます。そのように心がけておくことは悪くない習慣ですが、通常、プログラムの終了時には全てのメモリ領域は自動的に解放されるので、実用上はプログラムの終了直前まで使われているメモリを明示的に解放する必要はありません。
「malloc」と「free」の対応のポリシーに係わらず、実行途中でどこからも参照不可能になるメモリ領域は自分で解放しなければなりません。参照不可能なメモリ領域ができてしまうことをメモリリークと言います。長時間稼動させるプログラムでは、メモリリークが起こらないように注意しなければなりません。メモリリークを解消する最も有効な手段は、「malloc」で確保したメモリ領域は全て「free」で解放することです。メモリリークがあると、「free」できないはずです。
「sizeof」は型のデータのサイズを測る演算子です。仕様上、「sizeof(char)」の計算結果は常に1です。「sizeof」の引数は通常は型ですが、変数などを書くこともできます。引数に変数を書いた場合、計算結果はその変数の型を引数に書いた場合と同じです。文字列に「sizeof」を適用しても文字列の長さは得られません。
関数定義の中で、変数宣言や配列宣言の頭に「static」と書いてあることがあります。
char *tmp_copy(char *s){
static char buf[10];
strcpy(buf, s);
return buf;
}
通常、関数内で宣言された変数や配列は関数適用の実行が終わるとメモリから失われてしまいますが、このように宣言された変数や配列は関数適用の実行後も外部からその値を利用することが可能になります。外部からそのようなデータを参照するには、データへのポインタを関数の返り値にするか別の方法でポインタを読み出せるようにする必要があります。また、何度関数が呼び出されたしても、その変数のために常に同じ領域を利用します。役割としては、大域変数と近いものです。自分では、「static」を使うコードは書かない方がよいでしょう。
ちなみに、関数定義における関数自身の型の部分に「static」と書いてあるものは、関数定義内部の「static」とは意味が異なります。