データ数が増えてくると困るのが、メモリ管理です。例えば「複数のデータを扱うのに配列を用意したいが、使うたびにデータの個数が違う。」なんて時には、どんな時にも対応できるよう、限りなく大きな配列を用意しておくのも1つの手です。しかし、1000個分の配列を用意したのに実際のデータは10個しかなかった、なんて時には残り990個分はメモリの無駄遣いになってしまいます。そんな時に便利なのがこの章で紹介する
なお、今回使う「malloc」「free」関数は「stdlib.h」に定義されています。
/*01*/ #include <stdio.h>
/*02*/ #include <stdlib.h>
/*03*/
/*04*/ main()
/*05*/ {
/*06*/ int *p;
/*07*/
/*08*/ p = (int *)malloc(sizeof(int));
/*09*/ printf("何か整数を入力してください。 -> ");
/*10*/ scanf("%d", p);
/*11*/ printf("その数の自乗は %d です。\n", (*p)*(*p));
/*12*/
/*13*/ free(p);
/*14*/ return 0;
/*15*/ }
/*16*/
まず、扱うデータの型のポインタを用意しておきます。
ポインタ = (型キャスト)malloc(バイト数);
例:
p = (int *)malloc(4);
/*型キャストの際、ポインタを示す「*」記号を忘れないで下さい*/
sizeof(int); /*「4」を返す*/ sizeof(x); /*x が int 型の変数なら「4」を返す*/
また、メモリ不足などで充分な領域が確保できなかった場合、malloc 関数は「NULL」を返すので、これを使ってエラー時の処理を制御するといいでしょう。
if((p = (int *)malloc(sizeof(int))) == NULL){
printf("メモリ確保に失敗しました。終了します。\n");
return 0;
}
最後に、malloc 関数を使って自分で確保した領域は、自分で開放してやらなければなりません(必須:開放を忘れると、プログラム終了後もメモリが無駄に使われつづけます)。方法は、free 関数を使って、その引数にポインタを書いてやるだけです。しかし、この関数を書き忘れてもコンパイラはエラーを出してくれないので注意してください。また、自分で確保したもの以外(つまり普通に宣言された変数など)を、この関数を使って勝手に開放してしまうのはとんでもないエラーなので、しないで下さい。
次の例は、最初にデータの個数を入力して、その分だけメモリを確保するものです。注意する点は1箇所だけです。
/*01*/ #include <stdio.h>
/*02*/ #include <stdlib.h>
/*03*/
/*04*/ main()
/*05*/ {
/*06*/ int *data, *s;
/*07*/ float ave = 0;
/*08*/ int i, n;
/*09*/
/*10*/ printf("平均を求めます。\n");
/*11*/ printf("何人分のデータを入力しますか? -> ");
/*12*/ scanf("%d", &n);
/*13*/ if((data = (int *)malloc(n*sizeof(int))) == NULL){
/*14*/ printf("メモリ確保に失敗しました。終了します。\n");
/*15*/ return 0;
/*16*/ }
/*17*/
/*18*/ s = data;
/*19*/ printf("得点を入力してください。\n");
/*20*/ for(i = 0; i < n; i++){
/*21*/ printf("%d 人目 -> ", i + 1);
/*22*/ scanf("%d", s);
/*23*/ s++;
/*24*/ }
/*25*/
/*26*/ s = data;
/*27*/ printf("入力されたデータは、\n");
/*28*/ for(i = 0; i < n; i++){
/*29*/ printf("%d 人目 -> %d\n", i + 1, *s);
/*30*/ ave += *s;
/*31*/ s++;
/*32*/ }
/*33*/ ave /= n;
/*34*/ printf("平均は %.2f です。\n", ave);
/*35*/
/*36*/ free(data);
/*37*/ return 0;
/*38*/ }
今回、malloc 関数で確保するバイト数は「(データの個数) * (型のサイズ)」です。この例で、入力されたデータの個数が5個ならば、「5*sizeof(int)」、すなわち32ビット環境であれば int は4バイトなので「20」バイトの領域がメモリ上に確保されます。そして、その領域は型キャストで int 型に直され、つまり int 型の領域 5個になって、その先頭アドレスがポインタ data に入ります。
後はこれまでどおりの操作ですね。(data は先頭アドレスを保持しておくため、実際にはポインタ s で操作します。)
では、構造体の場合はどうでしょうか?sizeof に構造体の型を書いてそのサイズを求め、その構造体に型キャストするだけです。特に難しい点はありませんので、今回は例となるソースを載せるだけとしておきます。
/*01*/ #include <stdio.h>
/*02*/ #include <stdlib.h>
/*03*/
/*04*/ struct score{
/*05*/ char name[32];
/*06*/ int math;
/*07*/ int english;
/*08*/ int kei;
/*09*/ };
/*10*/
/*11*/ main()
/*12*/ {
/*13*/ struct score *a, *p;
/*14*/ int i, n;
/*15*/
/*16*/ printf("何人分のデータを入力しますか? -> ");
/*17*/ scanf("%d", &n);
/*18*/ if((a = (struct score *)malloc(n*sizeof(struct score))) == NULL){
/*19*/ printf("メモリ確保に失敗しました。終了します。\n");
/*20*/ return 0;
/*21*/ }
/*22*/
/*23*/ p = a;
/*24*/ printf("名前 英語の点 数学の点 を入力してください。\n");
/*25*/ for(i = 0; i < n; i++){
/*26*/ scanf("%s%d%d", p->name, &p->math, &p->english);
/*27*/ p->kei = p->math + p->english;
/*28*/ p++;
/*29*/ }
/*30*/
/*31*/ p = a;
/*32*/ putchar('\n');
/*33*/ for(i = 0; i < n; i++){
/*34*/ printf("%s の合計点は %d です。\n", p->name, p->kei);
/*35*/ p++;
/*36*/ }
/*37*/
/*38*/ free(a);
/*39*/ return 0;
/*40*/ }