4 ポインターのポインターを使おう

さて、それでどうするか、という最初の問題に戻るけれど、 C の場合はポインターのポインターを使うことを勧める。
#include <stdlib.h> // malloc() に必要

int main(void)
{
  int m,n, i;
  double **a;     // doubleへのポインターのポインター
  double *a_data; // doubleへのポインター
  printf("m,n="); scanf("%d%d", &m, &n);
  a = malloc(sizeof(double *) * m); // doubleへのポインター m 個分の領域確保
  a_data = malloc(sizeof(double) * (m * n)); // double m*n 個分の領域確保
  for (i = 0; i < m; i++)
    a[i] = a_data + (i * n);
  // これで a[i][j] (0≦i≦m-1, 0≦j≦n-1) が使える
すごく面倒に感じるかもしれないが、こういうのは関数にしてしまえば良い。 次のコードでは、 new_matrix() という関数を作って、その使い方を示している。
#include <stdlib.h> // malloc() のため

typedef double *vector;
typedef vector *matrix; // matrix って行列という意味です。

// m行n列の行列 (あるいは2次元格子上の数値データ) を記憶する領域の割り当て
matrix new_matrix(int m, int n)
{
  matrix a;
  vector a_data;
  int i;
  a = malloc(sizeof(double *) * m);
  if (a == NULL) // せっかくなのでエラー・チェックも
    return NULL;
  a_data = malloc(sizeof(double) * (m * n)); // 必要十分なサイズ
  if (a_data == NULL) {
    free(a);
    return NULL;
  }
  for (i = 0; i < m; i++)
    a[i] = a_data + (i * n);
  return a;
}

void delete_matrix(matrix a)
{
  free(a[0]); free(a); // a[0] は a_data であるから
}

int main(void)
{
  int m,n;
  matrix a, b, c;
  printf("m,n="); scanf("%d%d", &m, &n);
  a = new_matrix(m, n); // 関数にしておけば3つ用意するのも簡単
  b = new_matrix(m, n);
  c = new_matrix(m, n);
  if (a == NULL || b == NULL || c == NULL) {
    ...
  }
  ...
  free_matrix(a); free_matrix(b); free_matrix(c); // 要らなくなったら解放

(int main(void) の前はコピペして使えば良い。)

このやり方の特徴をまとめておく。

(1)
可変長配列の機能は使っていないので、Cのどの規格でも動く。
(ポインターは、古くから C に備わっている機能で、決してなくならないだろう。)
(2)
ポインター $ \texttt{m}+1$ 個だけメモリーが (追加で) 必要になる 1。 -- 大したことではない。
(3)
関数を自作する場合、引数のやり取りは簡単である。
void mulmat(int n, matrix a, matrix b, matrix c)
{
  int i, j, k;
  double s;
  // 略 for (i=0; i<n; i++) for (j=0; j<n; j++) { s=0.0; for (k=0;...
}

自作でない関数を使う場合にも、少しの工夫で対応できる場合が多い。 例えば、上で話題に出した、GLSC の g_contln()g_hidden() を使う場合、 2次元格子上の数値データが
   u = new_matrix(nx+1, ny+1);
として確保された u という matrix に記憶されているとして
  g_hidden(1.0, 1.0, 0.4, -1.0, 1.0, 5.0, 25.0, 20.0, 20.0, 20.0,
           150.0, 100.0, u[0], nx + 1, ny + 1,
           1, G_SIDE_NONE, 2, 1);
のように (配列を渡すべきところで) u[0] を渡せば良い (new_matrix() の中の a_data が渡されることになる)。

桂田 祐史
2017-03-06