二次元配列を動的に割り当てる（実行時に大きさが決まる二次元配列，構造体二次元配列）

# 動的な二次元配列を実行時に確保
実行時に大きさが決まる配列をC言語で書いてみます。これは動的な二次元配列の確保と表現されます。`calloc`関数を使います。

## calloc関数
Cell Allocation（メモリセルの配置）関数です。空きメモリから必要なメモリブロックを確保します。[こちらなどに情報](http://www9.plala.or.jp/sgwr-t/lib/calloc.html)があります。

## 実行時に確保と開放を行うサンプルプログラム（一次元配列）
一次元配列はよくあるパターンです。メモリブロックを確保して，その先頭アドレスをポインタ変数に代入します。`calloc`は確保したメモリブロックの先頭アドレスを返すだけで，何に使われるか分からないので，`(int *)`としてキャストしています。int型の要素が代入される配列を指すポインタ（先頭アドレス）として，`a`に代入しています。使用し終わったら`free`関数を使ってメモリブロックを開放しています。

```c:一次元配列の確保
#include<stdio.h>
#include<stdlib.h>

int main()
{
    int *a;
    unsigned h=10;
    unsigned i;

    a = (int *)calloc(h,sizeof(int));

    for(i=0;i<h;i++){
        a[i]=i;
        printf("%d ",a[i]);
    }
    printf("\n");

    free(a);

    return 0;
}
```

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146927/d632a533-5615-3e7f-3096-0e5d1836c49d.png)

## 実行時に確保と開放を行うサンプルプログラム（二次元配列）
`int`型の要素が`h`個代入できる大きさの一次元配列を`v`個用意しています。使用し終わったら`free`関数を使ってメモリブロックを開放しています。開放する順番は逆順になりますので，注意してください。`calloc`は確保したメモリブロックの先頭アドレスを返すだけで，何に使われるか分からないので，`int **`や`int *`としてキャストしています。なお，`int **a`と`int** a`は同じです。前者の方が古い書き方です。

```c:二次元配列の確保
#include<stdio.h>
#include<stdlib.h>

int main()
{
    int **a;
    unsigned v=5,h=10;
    unsigned i,j;

    a = (int **)calloc(v,sizeof(int *));
    for(i=0;i<v;i++){
        a[i] = (int *)calloc(h,sizeof(int));
    }
    
    for(i=0;i<v;i++){
        for(j=0;j<h;j++){
            a[i][j] = i*j;
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }

    for(i=0;i<v;i++){
        free(a[i]);
    }
    free(a);

    return 0;
}
```


![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146927/9cd23277-4f41-5b7c-66fd-a6d674f2965b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146927/90c4cbe3-e3ed-c359-43b8-1e03a94e5af0.png)

<font color="red">**上の図で，-456は間違いです。16074が正しくなります。**</font>

## 配列の確保と開放を行う専用関数を呼ぶサンプルプログラム
`main`関数で`makeArray`関数と`freeArray`関数を呼び出しています。その際の引数ですが，ポインタを指すポインタのアドレス`&a`･･･つまりポインタのポインタを「指すもの」なので，ポインタのポインタのポインタとなり，引数を受け取る側では`***`となります。そして`a`ですが，上のサンプルプログラムでは，一次元配列（長さ`h`個）の先頭アドレスを`v`個だけ格納する一次元配列の先頭アドレスそのものでした。今回は，さらにその先頭を指すアドレスが格納されているのが'a'となりますので，その`a`に代入されているアドレス先にある実態を表現する`*a`に割りつけます。つまり`*a = (int**)calloc(v, sizeof(int*));`と書きます。`for`文内でのメモリの割り当てですが，`*a[i]`と書きたくなりますが，`*`より`[`の方が結びつきの方が強いので，`*(a[i])`と同じになってしまい，エラーになります。そのため` (*a)[i]`と書きます。

```c:二次元配列の確保（メモリ確保と開放をする関数）
#include<stdio.h>
#include<stdlib.h>

void makeArray(int*** a, unsigned v, unsigned h)
{
    unsigned i;
    *a = (int**)calloc(v, sizeof(int*));
    for (i = 0; i < v; i++) {
        (*a)[i] = (int*)calloc(h, sizeof(int));
        //*a[i] = (int*)calloc(h,sizeof(int)); // *(a[i])と同じなのでエラー
    }
}

void freeArray(int*** a, unsigned v)
{
    unsigned i;
    for (i = 0; i < v; i++) {
        free((*a)[i]);
    }
    free(*a);
}

int main()
{
    int** a;
    unsigned v = 5, h = 10;
    unsigned i, j;

    makeArray(&a, v, h);

    for (i = 0; i < v; i++) {
        for (j = 0; j < h; j++) {
            a[i][j] = i * j;
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }

    freeArray(&a,v);
    return 0;
}
```

## 構造体の二次元配列のサンプルプログラム
これまでに書いた二つのサンプルプログラムの`int`を構造体の型に変えるだけなので，省略します。

## 構造体のメンバとして動的な二次元配列を割り当てる
二次元配列のサイズと`int** a;`をメンバに持つ構造体について，`a`に二次元配列を割り当てる`makeArray`関数と`freeArray`関数を書いてみます。上の二つのサンプルプログラムの応用になります。これは後述のように矢印演算子（アロー演算子）を使った方が綺麗に書けます。

```c:構造体のメンバに動的に二次元配列を割り当てる
#include<stdio.h>
#include<stdlib.h>

typedef struct{
    unsigned v,h;
    int** a;
}stType;

void makeArray(stType* st)
{// 「.」より「*」の方が結合が弱いので()でくくる
    unsigned i;
    (*st).a = (int**)calloc((*st).v,sizeof(int*));
    for(i=0;i<(*st).v;i++){
        (*st).a[i] = (int*)calloc((*st).h,sizeof(int));
    }
}

void freeArray(stType* st)
{
    unsigned i;
    for(i=0;i<(*st).v;i++){
        free((*st).a[i]);
    }
    free((*st).a);
}

int main()
{
    stType st;
    st.v=5;
    st.h=10;
    unsigned i,j;

    makeArray(&st);
    
    for(i=0;i<st.v;i++){
        for(j=0;j<st.h;j++){
            st.a[i][j]=i*j;
            printf("%d ",st.a[i][j]);
        }
        printf("\n");
    }
    
    freeArray(&st);
    return 0;
}
```

矢印演算子を使って書くと次のようになります。

```c:構造体のメンバに動的に二次元配列を割り当てる（アロー演算子）
#include<stdio.h>
#include<stdlib.h>

typedef struct {
    unsigned v, h;
    int** a;
}stType;

void makeArray(stType* st)
{// 矢印演算子を使った例
    unsigned i;
    st->a = (int**)calloc(st->v, sizeof(int*));
    for (i = 0; i < st->v; i++) {
        st->a[i] = (int*)calloc(st->h, sizeof(int));
    }
}

void freeArray(stType* st)
{
    unsigned i;
    for (i = 0; i < st->v; i++) {
        free(st->a[i]);
    }
    free(st->a);
}

int main()
{
    stType st;
    st.v = 5;
    st.h = 10;
    unsigned i, j;

    makeArray(&st);

    for (i = 0; i < st.v; i++) {
        for (j = 0; j < st.h; j++) {
            st.a[i][j] = i * j;
            printf("%d ", st.a[i][j]);
        }
        printf("\n");
    }

    freeArray(&st);
    return 0;
}
```


## 長方形でない（2番目の要素番号が一定でない）配列（構造体配列と構造体のメンバーとしての配列の両方を動的に割り当てる二次元配列）
下記のように構造体のポインタ変数を用意し，そのポインタ変数にメモリを割り当て，構造体のメンバにメモリを割り当てていくことで，長方形でない二次元配列を作ることができます。またv[i].h[j]の様にして各要素にわかりやすくアクセスできます。

```C:構造体とメンバを使った二次元配列の動的な割り当て
#include<stdio.h>
#include<stdlib.h>

typedef struct {
    unsigned unitNum;//正方形の二次元配列ならこのメンバは不要
    int *unit;
}nnType;

int main()
{
    nnType *layer;
    unsigned layerNum=5;//要素数は適当に代入
    unsigned i, j;

    layer = (nnType*)calloc(layerNum, sizeof(nnType));//メモリ割り当て
    for (i = 0; i < layerNum; i++) {
        layer[i].unitNum = 3;//要素数は適当に代入，layer毎に異なる要素数でもok
    }
    for (i = 0; i < layerNum; i++) {
        layer[i].unit = (int*)calloc(layer[i].unitNum, sizeof(int));//メモリ割り当て
    }
    for (i = 0; i < layerNum; i++) {
        for (j = 0; j < layer[i].unitNum; j++) {
            layer[i].unit[j] = i * j;//動作チェックのため適当に代入
        }
    }
    for (i = 0; i < layerNum; i++) {
        for (j = 0; j < layer[i].unitNum; j++) {
            printf("%3d ",layer[i].unit[j]);//要素の表示
        }
        printf("\n");
    }

    for (i = 0; i < layerNum; i++) {
        free(layer[i].unit);//メモリ開放
    }
    free(layer);//メモリ開放

    return 0;
}
```

上のプログラムで，メモリ割り当てと開放を関数化するプログラムをもうすぐ掲載