第32回
データ構造(11)~構造体を前後に移動できる双方向リンク

メモリの解放~free関数

malloc関数で確保したメモリは、プログラムが実行されている間、ずっと使用中となっています。メモリを独り占めしてはいけません。必要のなくなったメモリは、明示的に解放しなければなりません。

用済みのメモリは解放する

プログラムがmalloc関数で動的にメモリを確保すると、そのメモリ領域はプログラムが終了するまで他のプログラムでは使用できなくなります。動的に確保されたメモリは、本来なら明示的に解放されなければなりません。

前回紹介したサンプルのようにシンプルな構造であれば、大量のメモリを消費して他のプログラムの迷惑となることはないでしょう。また、基本的にシングルタスクであるDOSの場合なら、他のプログラムを意識する必要すらありません。

しかし、マルチタスク環境でたくさんのデータを保持する構造体を大量に生成する場合、用が済んだら──構造体を利用する必要がなくなったら、即座にそのためのメモリを解放し、他のプログラムで利用できるようにしなければなりません ※1
メモリの解放を行わないプログラムは、いわゆる「メモリ食い」となってしまいます。また、メモリの解放処理が不完全で、プログラムの終了後にもメモリが解放されない場合は、いわゆる「メモリリーク(メモリ漏れ)」という状態を引き起こします

free関数でメモリを解放

malloc関数で確保されたメモリは、free関数で解放できます。free関数の書式は以下のようになります。
free(<ポインタ>);

free関数の戻り値はvoid型で、<ポインタ>で示すメモリ領域を開放します。<ポインタ>の示す型も、どの型であっても構いません ※2

前回紹介した構造体の一方向リンクで氏名と点数を表示するプログラムには、このメモリを解放する処理がありませんでした。リスト1は、構造体のリンクをたどって内容(氏名と点数)を表示した後に、構造体のために確保したメモリを解放する処理を付け加えたものです。

return (0);とした最後の処理の直前で、whileループを使って構造体のリンクをたどり、free関数でメモリを順に解放しています。

  result *q;
    :
  p = start; -------- ポインタを先頭に位置付ける
  while (p != NULL) {
    q = p->next;  /* 次へのポインタを保存 */
    free(p);
    p = q;
  }
元々ポインタは、それの示す変数の型に左右されません。ポインタ自体が型の占有するメモリサイズの情報を持っているからです

表示も解放も仕組みは同じ

リスト1でメモリを解放している仕組みは、そのすぐ上で行っている『構造体のリンクをたどって内容を表示する』処理と似ています。

  printf("ID\tName\tScore\n");
  p = start; -------- ポインタを先頭に位置付ける
  while (p != NULL) {
    printf("%04d\t%s\t%d\n", p->id, p->name, p->score);
    p = p->next;
  }

whileループの中でprintf関数を使ってメンバの値を出力するところと、free関数でメモリを解放するところが異なるだけです。

この形は、構造体の一方向リンクを扱う際の定番と言えます。パターンとして覚えておくといいでしょう。

リスト1:氏名と点数の配列を構造体のリンクに格納して表示するプログラム
(メモリの解放処理を追加)(ex3201.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)
{
  /* 構造体 result を定義 */
  typedef struct _result{
    short int id;
    char name[16];
    short int score;
    struct _result *next;
  } result;

  int i;              /* ループカウンタ */
  result *p, *start;  /* 構造体のポインタ */
  result *q;          /* メモリ解放用 */

  /* 元データを配列で準備 */
  char _name[5][16] = {"Taro","Hanako","Sayaka","Miki","Hiroshi"};
  short int _score[5] = {50, 60, 70, 80, 30};

  for (i=0; i<5; i++) {
    if (i==0) {
      /* 先頭の構造体を生成 */
      p = malloc(sizeof(result));
      start = p;  /* 先頭を保存 */
    } else {
      p->next = malloc(sizeof(result));
      p = p->next;
    }
    /* IDはカウンタ変数から生成 */
    p->id = (short int)i+1;
    /* 2項目を代入 */
    strcpy(p->name, _name[i]);
    p->score = _score[i];
    p->next = NULL;
  }

  /* 構造体のリンクをたどって先頭からメンバを表示 */
  printf("ID\tName\tScore\n");
  p = start;  /* ポインタを先頭に */
  while (p != NULL) {
    printf("%04d\t%s\t%d\n", p->id, p->name, p->score);
    p = p->next;
  }

  /* 構造体のリンクをたどってメモリを解放する */
  p = start; -------- ポインタを先頭に位置付ける
  while (p != NULL) {
    q = p->next;  /* 次へのポインタを保存 */
    free(p);
    p = q;
  }

  return (0);
}