多次元配列の動的確保

毎度のことながら備忘録シリーズ。

お題:Cで多次元配列の動的確保ってどうやるんだっけ?

正直言って情報系の大学院生なのに分からないとか相当恥ずかしい。

前の大学が悪いんだ!こんなこと教えてくれなかったぜ!と言いたいところだけど、独学で勉強しなかった自分も悪い。

なので恥ずかしいことだけどあえて書く!

そして忘れないように(and 忘れたときのために)しっかりと書く!

再度お題:Cで多次元配列の動的確保ってどうやるんだっけ?

http://www.booran.com/menu/c/multi_pointer.htmlを参考にしました。分かりやすかった!

答えは簡単でした、ポインタのポインタを用意してmallocしてアドレス突っ込んでやるだけ!

そう、Javaと同じでした。

Javaで

[java]
String[] stringArray = new String[10];
[/java]

ってやってもそのままじゃ使えませんよね。

[java]
stringArray[0] = new String();
[/java]

をやらないと実体が無いよ・・・と。

Cも同じで例えば3*3*3の3次元配列を動的に作ろうとしたときは以下のような感じで作る。

[c]
#include <stdio.h>
#include <stdlib.h>

int main(void){
  int ***malloc_array;
  int i, j, k;

  //3次元配列の確保
  malloc_array = (int ***)malloc(sizeof(int**) * 3);
  for(i=0; i<3; i++){
    malloc_array[i] = (int **)malloc(sizeof(int *) * 3);
    for(j=0; j<3; j++){
      malloc_array[i][j] = (int *)malloc(sizeof(int) * 3);
    }
  }

  //0クリア
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
	malloc_array[i][j][k] = 0;
      }
    }
  }

  //3次元配列として扱える
  malloc_array[0][0][1] = 1;

  //print malloc_array
  printf("print malloc_array\n");
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
        printf("%d ", malloc_array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }
  printf("\n");

  return 0;
}
[/c]

出力結果

print malloc_array
0 1 0
0 0 0
0 0 0

0 0 0
0 0 0
0 0 0

0 0 0
0 0 0
0 0 0

と、まあ面倒っちゃ面倒だけど一応作れるようにはなった。

けどこれだとmallocを何度も呼び出しているのでアドレスがきっちり揃っていない。

アドレスを出力するコードを追加して実行してみる。

[c]
  //print malloc_array addresses
  printf("print malloc_array addrsses\n");
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
        printf("%x ", &malloc_array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }
  printf("\n");

[/c]

print malloc_array addrsses
e2bc050 e2bc054 e2bc058
e2bc070 e2bc074 e2bc078
e2bc090 e2bc094 e2bc098

e2bc0d0 e2bc0d4 e2bc0d8
e2bc0f0 e2bc0f4 e2bc0f8
e2bc110 e2bc114 e2bc118

e2bc150 e2bc154 e2bc158
e2bc170 e2bc174 e2bc178
e2bc190 e2bc194 e2bc198

すごく・・・とびとびです・・・。

ポインタ変数を++すると型分だけ先に進んで次の内容(配列の1個先)が見れるってのがありますよね。

3次元配列と言えど、結局は1次元だから上記の配列を1次元と見なして++でどんどん次を見ていくとき、こんなアドレス状況じゃあ使える訳ないですね。

という訳で改良。ようは、どかっと取ればいい。

[c]
#include <stdio.h>
#include <stdlib.h>

int main(void){
  int ***malloc_array;
  int i, j, k;
  int count = 0;
  int *p;

  //3次元配列の確保
  malloc_array = (int ***)malloc(sizeof(int**) * 3 );
  for(i=0; i<3; i++){
    malloc_array[i] = (int **)malloc(sizeof(int *) * 3);
  }
  //intの実体(配列)をどかっと確保
  malloc_array[0][0] = (int *)malloc(sizeof(int) * 27);

  malloc_array[0][1] = malloc_array[0][0] + 3;
  malloc_array[0][2] = malloc_array[0][1] + 3;
  malloc_array[1][0] = malloc_array[0][2] + 3;
  malloc_array[1][1] = malloc_array[1][0] + 3;
  malloc_array[1][2] = malloc_array[1][1] + 3;
  malloc_array[2][0] = malloc_array[1][2] + 3;
  malloc_array[2][1] = malloc_array[2][0] + 3;
  malloc_array[2][2] = malloc_array[2][1] + 3;

  p = &malloc_array[0][0][0];

  //0クリア
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
	malloc_array[i][j][k] = 0;
      }
    }
  }

  //3次元配列として扱える
  malloc_array[0][0][1] = 1;

  //print malloc_array
  printf("print malloc_array\n");
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
        printf("%d ", malloc_array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }
  printf("\n");

  //print malloc_array addresses
  printf("print malloc_array addrsses\n");
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
	printf("%x ", &malloc_array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }
  printf("\n");

  //print malloc_array pointer addresses
  printf("print malloc_array addrsses\n");
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      printf("%x \n", &malloc_array[i][j]);
    }
    printf("\n");
  }
  printf("\n");

  //malloc_arrayを適当な数字で埋める
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      for(k=0; k<3; k++){
	malloc_array[i][j][k] = ++count;
      }
    }
  }

  //上手く行く、1次元配列として扱える
  printf("forで27回ループしつつp++してゆく\n");
  for(i=0; i<27; i++){
    printf("[%d]=%d\n", i, *p++);
  }

  return 0;
}

[/c]

出力結果

print malloc_array
0 1 0
0 0 0
0 0 0

0 0 0
0 0 0
0 0 0

0 0 0
0 0 0
0 0 0

print malloc_array addrsses
1321b090 1321b094 1321b098
1321b09c 1321b0a0 1321b0a4
1321b0a8 1321b0ac 1321b0b0

1321b0b4 1321b0b8 1321b0bc
1321b0c0 1321b0c4 1321b0c8
1321b0cc 1321b0d0 1321b0d4

1321b0d8 1321b0dc 1321b0e0
1321b0e4 1321b0e8 1321b0ec
1321b0f0 1321b0f4 1321b0f8

print malloc_array addrsses
1321b030
1321b038
1321b040

1321b050
1321b058
1321b060

1321b070
1321b078
1321b080

forで27回ループしつつp++してゆく
[0]=1
[1]=2
[2]=3
[3]=4
[4]=5
[5]=6
[6]=7
[7]=8
[8]=9
[9]=10
[10]=11
[11]=12
[12]=13
[13]=14
[14]=15
[15]=16
[16]=17
[17]=18
[18]=19
[19]=20
[20]=21
[21]=22
[22]=23
[23]=24
[24]=25
[25]=26
[26]=27

きれいに揃ってるしちゃんと++できました。

でも多次元配列を1次元と見なして++していいんだろうか。

実験したところmallocじゃなく宣言で多次元配列用意するとほとんど連続したアドレスが割り当てられるみたいだけど・・・

ここら辺は言語仕様読まなくちゃ分からない気がする。

まだ詰めが甘い気がするけど、とりあえず多次元配列の動的確保は完了!

また少し成長した気がします。

コメントを残す

メールアドレスが公開されることはありません。