C言語における多次元配列

double a[100][100] のような配列の確保に代わる方法の例。

関連記事 C言語への導入

vectorの使用

since 2010-11-16

C++が当たり前の時代なので、素直に書いてみようと思ったのが下記の例。

  • int と double と string について、5次元配列まで書いてみた。
  • テンプレートを上手に使う書き方はあるが、C++の機能にこだわるとコンパイラの互換性が損なわれるので。。
  • 関数で多次元配列を受け取るときに&をつけて参照渡しにしている。これをつけないと、return したときに値がもとに戻ってしまう。
// allocmat.cpp
 
#include <stdio.h>
#include "allocmat.hpp"
 
void test_array2(DoubleArray2 &a, int u, int v)
{
    for (int i=0; i<u; i++) 
        for (int j=0; j<v; j++) 
            a[i][j] = i + j/10.0;
    a[0][0] = 99.9;
    for (int i=0; i<u; i++) {
        for (int j=0; j<v; j++) 
            printf(" %05.1f",a[i][j]);
        printf("\n");
    }
    printf("\n");
}
 
void test_array3(DoubleArray3 &a, int u, int v, int w)
{
    for (int i=0; i<u; i++) {
        test_array2(a[i], v, w);
    }
}
 
void test_array4(DoubleArray4 &a, int u, int v, int w, int x)
{
    for (int i=0; i<u; i++) {
        test_array3(a[i], v, w, x);
    }
}
 
void test_array5(DoubleArray5 &a, int u, int v, int w, int x, int y)
{
    for (int i=0; i<u; i++) {
        test_array4(a[i], v, w, x, y);
    }
}
 
int main(void)
{
    int u = 2;
    int v = 3;
    int w = 4;
    int x = 5;
    int y = 6;
 
    DoubleArray2 a2 = AllocDoubleArray2(u, v);
    test_array2(a2, u, v);
 
    DoubleArray3 a3 = AllocDoubleArray3(u, v, w);
    test_array3(a3, u, v, w);
 
    DoubleArray4 a4 = AllocDoubleArray4(u, v, w, x);
    test_array4(a4, u, v, w, x);
 
    DoubleArray5 a5 = AllocDoubleArray5(u, v, w, x, y);
    test_array5(a5, u, v, w, x, y);
 
    IntegerArray5 ia5 = AllocIntegerArray5(u, v, w, x, y);
    ia5[1][2][3][4][5] = 100;
 
    StringArray5 sa5 = AllocStringArray5(u, v, w, x, y);
    sa5[1][2][3][4][5] = "hello";
 
    return 0;
}
// allocmat.hpp
 
using namespace std;
#include <vector>
#include <string>
 
typedef vector< double > DoubleArray;
typedef vector< DoubleArray > DoubleArray2;
typedef vector< DoubleArray2 > DoubleArray3;
typedef vector< DoubleArray3 > DoubleArray4;
typedef vector< DoubleArray4 > DoubleArray5;
 
DoubleArray AllocDoubleArray(int u)
{
    DoubleArray ar(u);
    return ar;
}
 
DoubleArray2 AllocDoubleArray2(int u, int v)
{
    DoubleArray2 mat;
    for (int i = 0; i < u; i++) {
        DoubleArray ar(v);
        mat.push_back(ar);
    }
    return mat;
}
 
DoubleArray3 AllocDoubleArray3(int u, int v, int w)
{
    DoubleArray3 mat;
    for (int i = 0; i < u; i++) {
        DoubleArray2 ar = AllocDoubleArray2(v, w);
        mat.push_back(ar);
    }
    return mat;
}
 
DoubleArray4 AllocDoubleArray4(int u, int v, int w, int x)
{
    DoubleArray4 mat;
    for (int i = 0; i < u; i++) {
        DoubleArray3 ar = AllocDoubleArray3(v, w, x);
        mat.push_back(ar);
    }
    return mat;
}
 
DoubleArray5 AllocDoubleArray5(int u, int v, int w, int x, int y)
{
    DoubleArray5 mat;
    for (int i = 0; i < u; i++) {
        DoubleArray4 ar = AllocDoubleArray4(v, w, x, y);
        mat.push_back(ar);
    }
    return mat;
}
 
typedef vector< int > IntegerArray;
typedef vector< IntegerArray > IntegerArray2;
typedef vector< IntegerArray2 > IntegerArray3;
typedef vector< IntegerArray3 > IntegerArray4;
typedef vector< IntegerArray4 > IntegerArray5;
 
IntegerArray AllocIntegerArray(int u)
{
    IntegerArray ar(u);
    return ar;
}
 
IntegerArray2 AllocIntegerArray2(int u, int v)
{
    IntegerArray2 mat;
    for (int i = 0; i < u; i++) {
        IntegerArray ar(v);
        mat.push_back(ar);
    }
    return mat;
}
 
IntegerArray3 AllocIntegerArray3(int u, int v, int w)
{
    IntegerArray3 mat;
    for (int i = 0; i < u; i++) {
        IntegerArray2 ar = AllocIntegerArray2(v, w);
        mat.push_back(ar);
    }
    return mat;
}
 
IntegerArray4 AllocIntegerArray4(int u, int v, int w, int x)
{
    IntegerArray4 mat;
    for (int i = 0; i < u; i++) {
        IntegerArray3 ar = AllocIntegerArray3(v, w, x);
        mat.push_back(ar);
    }
    return mat;
}
 
IntegerArray5 AllocIntegerArray5(int u, int v, int w, int x, int y)
{
    IntegerArray5 mat;
    for (int i = 0; i < u; i++) {
        IntegerArray4 ar = AllocIntegerArray4(v, w, x, y);
        mat.push_back(ar);
    }
    return mat;
}
 
typedef vector< string > StringArray;
typedef vector< StringArray > StringArray2;
typedef vector< StringArray2 > StringArray3;
typedef vector< StringArray3 > StringArray4;
typedef vector< StringArray4 > StringArray5;
 
StringArray AllocStringArray(int u)
{
    StringArray ar(u);
    return ar;
}
 
StringArray2 AllocStringArray2(int u, int v)
{
    StringArray2 mat;
    for (int i = 0; i < u; i++) {
        StringArray ar(v);
        mat.push_back(ar);
    }
    return mat;
}
 
StringArray3 AllocStringArray3(int u, int v, int w)
{
    StringArray3 mat;
    for (int i = 0; i < u; i++) {
        StringArray2 ar = AllocStringArray2(v, w);
        mat.push_back(ar);
    }
    return mat;
}
 
StringArray4 AllocStringArray4(int u, int v, int w, int x)
{
    StringArray4 mat;
    for (int i = 0; i < u; i++) {
        StringArray3 ar = AllocStringArray3(v, w, x);
        mat.push_back(ar);
    }
    return mat;
}
 
StringArray5 AllocStringArray5(int u, int v, int w, int x, int y)
{
    StringArray5 mat;
    for (int i = 0; i < u; i++) {
        StringArray4 ar = AllocStringArray4(v, w, x, y);
        mat.push_back(ar);
    }
    return mat;
}

ダブルポインタの利用

下記の記事のサンプルに基づいて説明する(C++のテンプレートによる実装部分は割愛する):

http://www001.upp.so-net.ne.jp/isaku/tips/matrix.c.html

(1) allocmat.h (下記)を用意する。

/*
 * allocmat.h
 * http://www001.upp.so-net.ne.jp/isaku/tips/matrix.c.html
 */
#include <stdlib.h>
void*AllocMatrix(int s,int u,int v)
{
  int i,t=s*v; char**a,*b;
  a=(char**)malloc((sizeof*a+t)*u);
  if (a) b=(char*)(a+u); else return 0;
  for (i=0;i<u;i++,b+=t) a[i]=b;
  return a;
}
#define ALLOC_MATRIX(T,U,V) (T**)AllocMatrix(sizeof(T),U,V)
#define FREE(X) free((void *)X)
/* end of file */

(2) allocmat.c (下記)を用意する。

/*
 * allocmat.c
 * http://www001.upp.so-net.ne.jp/isaku/tips/matrix.c.html
 */
#include <stdio.h>
#include "allocmat.h"
 
void modify(double **b)
{
  b[0][0] = 99.99;
}
 
int main(int argc, char **argv)
{
  int u=14,v=13,i,j; 
  double **a = ALLOC_MATRIX(double, u, v);
  for (i=0;i<u;i++) for (j=0;j<v;j++) a[i][j]=i+j/100.0;
  modify(a);
  for (i=0;i<u;i++) {
    for (j=0;j<v;j++) printf(" %05.2f",a[i][j]);
    printf("\n");
  }
  FREE(a); return 0;
}

main() から modify() への受け渡しは a という変数の名前だけでよい。 メモリを確保するときに配慮がなされているので、この配列の大きさを指定する必要がない。

(3) 実行例

$ gcc allocmat.c
$ ./a.out
 99.99 00.01 00.02 00.03 00.04 00.05 00.06 00.07 00.08 00.09 00.10 00.11 00.12
 01.00 01.01 01.02 01.03 01.04 01.05 01.06 01.07 01.08 01.09 01.10 01.11 01.12
 02.00 02.01 02.02 02.03 02.04 02.05 02.06 02.07 02.08 02.09 02.10 02.11 02.12
 03.00 03.01 03.02 03.03 03.04 03.05 03.06 03.07 03.08 03.09 03.10 03.11 03.12
 04.00 04.01 04.02 04.03 04.04 04.05 04.06 04.07 04.08 04.09 04.10 04.11 04.12
 05.00 05.01 05.02 05.03 05.04 05.05 05.06 05.07 05.08 05.09 05.10 05.11 05.12
 06.00 06.01 06.02 06.03 06.04 06.05 06.06 06.07 06.08 06.09 06.10 06.11 06.12
 07.00 07.01 07.02 07.03 07.04 07.05 07.06 07.07 07.08 07.09 07.10 07.11 07.12
 08.00 08.01 08.02 08.03 08.04 08.05 08.06 08.07 08.08 08.09 08.10 08.11 08.12
 09.00 09.01 09.02 09.03 09.04 09.05 09.06 09.07 09.08 09.09 09.10 09.11 09.12
 10.00 10.01 10.02 10.03 10.04 10.05 10.06 10.07 10.08 10.09 10.10 10.11 10.12
 11.00 11.01 11.02 11.03 11.04 11.05 11.06 11.07 11.08 11.09 11.10 11.11 11.12
 12.00 12.01 12.02 12.03 12.04 12.05 12.06 12.07 12.08 12.09 12.10 12.11 12.12
 13.00 13.01 13.02 13.03 13.04 13.05 13.06 13.07 13.08 13.09 13.10 13.11 13.12

3次元版は http://www001.upp.so-net.ne.jp/isaku/tips/cubic.c.html を参考に作れる

(4) デバッグ

こうして確保したとしても2次元配列の要素アクセスにはバグが生じやすい。 ひとつのデバッグのテクニックは要素の read/write のラッパー関数を作ることである。

allocmat.c の改変例を以下に示す。

assert() は条件を満たさない場合にメッセージを表示して プログラムを停止してくれる関数であり、assert.h をインクルードして使う。

下記の例では modify() で set_foo(b, 0, 100, 99.99); のように配列サイズを超えた箇所のアクセスをしている。

/*
 * http://www001.upp.so-net.ne.jp/isaku/tips/matrix.c.html
 */
#include <stdio.h>
#include <assert.h>
#include "allocmat.h"
 
double get_foo(double **foo, int u, int v)
{
  assert(0 <= u); assert(v < 13); assert(0 <= u); assert(v < 13);
  return foo[u][v];
}
 
void set_foo(double **foo, int u, int v, double val)
{
  assert(0 <= u); assert(v < 13); assert(0 <= u); assert(v < 13);
  foo[u][v] = val;
}
 
void modify(double **b)
{
  set_foo(b, 0, 100, 99.99);
}
 
int main(int argc, char **argv)
{
  int u=14, v=13, i,j;
  double **a = ALLOC_MATRIX(double,u,v);
  for (i=0;i<u;i++)
    for (j=0;j<v;j++)
      set_foo(a, i, j, i+j/100.0);
  modify(a);
  for (i=0;i<u;i++) {
    for (j=0;j<v;j++)
      printf(" %05.2f", get_foo(a, i, j));
    printf("\n");
  }
  FREE(a); return 0;
}

実行すると以下のエラーが出てプログラムが止まる。

$ ./allocmat
allocmat: allocmat.c:17: void set_foo(double**, int, int, double): Assertion `v < 13' failed.
Aborted

このような仕組みを printf() の埋込みや gdb(デバッガ)の利用と併用すればよいだろう。

その他の実装

文献

allocmat.txt · 最終更新: 2010/11/16 23:28 by Takuya Nishimoto
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0