みなためラボ

【C言語】配列の領域の動的確保のやり方(1次元、2次元、3次元)

文字サイズ

はじめに

どうも! 高杉 皆為(@MinatameT)です。

この記事では、C言語での配列の領域の動的確保のやり方を説明しています。1次元配列、2次元配列、3次元配列の3種類を紹介します。

簡単な確認ですが、動的確保に対して「静的確保」では

int s[10];

というように、プログラム実行前から領域(サイズ)を指定していました。

これ(上記のソースコードの10の部分)を、プログラム実行後に決めよう……というのが動的確保ですよね。

動的確保にはポインターを用います。ポインターについては、次の記事をお読みください。ていねいに説明してあります。

長々と説明するよりも、簡単なプログラム例を見てもらったほうが早く理解できると思います。したがって、ソースコード中の「コメント文(//以降)」にも目を通しつつ、全体を確認していただきたいと思います。

それでは、配列の領域の動的確保のやり方を説明していきます。1次元から順番に確認していきましょう。

1次元配列の領域の動的確保をするプログラム例

ソースコード

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
  int *data; //ポインター変数(後で1次元配列化する。)
  int n; //1次元配列の要素数([☆])
  int e; //1次元配列の要素番号([☆])

  printf("配列の要素数(data[☆])を入力してください。>");
  scanf("%d",&n);

  //malloc関数で、メモリー領域を動的確保する。
  data = (int*)malloc(sizeof(int)*n); //int型のスペースを要素数([☆])の分だけ確保する。

  printf("整数値を入力してください。\n");
  for(e=0; e<n; e=e+1) //要素数([☆])の分だけループする。
  {
    printf("data[%d]の入力>",e);
    scanf("%d",&data[e]); //整数値の入力
  }

  printf("配列に格納された整数値は、以下のとおりです。\n");
  for(e=0; e<n; e=e+1) //要素数([☆])の分だけループする。
  {
    printf("data[%d] = %d\n",e,data[e]); //整数値の出力
  }

  free(data); //メモリー領域の解放

  return(0);
}

ソースコードの説明

1次元配列を作りたいので、*dataというポインター変数を用意し、それを1次元配列化します。

このプログラムの大まかな流れは、次のとおりです。

プログラムの流れ
  1. *dataなどの変数を用意する。
  2. 配列の要素数を入力させる。
  3. 入力された要素数の領域を自動で確保し、*dataを1次元配列化する。
  4. 1次元配列dataに整数値を、順番に入力していく。
  5. 1次元配列dataに入れられた整数値を、順番に表示していく。
  6. 配列の領域の解放をおこなう。

また、1次元配列のイメージ図は次のとおりです。

1次元配列のイメージ図

この図の場合は、data[0]からdata[2]までの領域を動的確保した……と考えてください。

領域の動的確保をしているのは、次の部分です。

  data = (int*)malloc(sizeof(int)*n); //int型のスペースを要素数([☆])の分だけ確保する。

この部分では、data(ポインター)に「n個(入力値)の要素数のint型領域」を確保させています。こうすることで、data[e]というように、1次元配列化することができます。

ただし、malloc関数で動的確保した領域は、free関数で最後に解放してあげる必要があります。

  free(data); //メモリー領域の解放

これで、dataが確保していた領域を解放することができます。

それでは、このプログラムの実行結果の例を見ていきましょう。

実行結果の例

配列の要素数(data[☆])を入力してください。>3
整数値を入力してください。
data[0]の入力>1
data[1]の入力>2
data[2]の入力>3
配列に格納された整数値は、以下のとおりです。
data[0] = 1
data[1] = 2
data[2] = 3

入力した整数値が、正しく表示されていることがわかります!

2次元配列の領域の動的確保をするプログラム例

ソースコード

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
  int **data; //2重ポインター変数(後で2次元配列化する。)
  int n1; //2次元配列の要素数([☆][])
  int n2; //2次元配列の要素数([][☆])
  int e1; //2次元配列の要素番号([☆][])
  int e2; //2次元配列の要素番号([][☆])

  printf("配列の要素数(data[☆][])を入力してください。>");
  scanf("%d",&n1);
  printf("配列の要素数(data[][☆])を入力してください。>");
  scanf("%d",&n2);

  //malloc関数で、メモリー領域を動的確保する。
  data = (int**)malloc(sizeof(int*)*n1); //int*型のスペースを要素数([☆][])の分だけ確保する。
  for(e1=0; e1<n1; e1=e1+1)
  {
    data[e1] = (int*)malloc(sizeof(int)*n2); //int型のスペースを要素数([][☆])の分だけ確保する。
  }

  printf("整数値を入力してください。\n");
  for(e1=0; e1<n1; e1=e1+1) //要素数([☆][])の分だけループする。
  {
    for(e2=0; e2<n2; e2=e2+1) //要素数([][☆])の分だけループする。
    {
      printf("data[%d][%d]の入力>",e1,e2);
      scanf("%d",&data[e1][e2]); //整数値の入力
    }
  }

  printf("配列に格納された整数値は、以下のとおりです。\n");
  for(e1=0; e1<n1; e1=e1+1) //要素数([☆][])の分だけループする。
  {
    for(e2=0; e2<n2; e2=e2+1) //要素数([][☆])の分だけループする。
    {
      printf("data[%d][%d] = %d\n",e1,e2,data[e1][e2]); //整数値の出力
    }
  }

  //メモリーの解放は、malloc関数のときの逆の順番でおこなう。
  for(e1=0; e1<n1; e1=e1+1)
  {
    free(data[e1]); //メモリー領域([][☆])の解放
  }
  free(data); //メモリー領域([☆][])の解放

  return(0);
}

ソースコードの説明

2次元配列を作りたいので、**dataというポインター変数を用意し、それを2次元配列化します。 2次元配列なので2重ポインターです。

このプログラムの大まかな流れは、次のとおりです。

プログラムの流れ
  1. **dataなどの変数を用意する。
  2. 配列の要素数を入力させる。
  3. 入力された要素数の領域を自動で確保し、**dataを2次元配列化する。
  4. 2次元配列dataに整数値を、順番に入力していく。
  5. 2次元配列dataに入れられた整数値を、順番に表示していく。
  6. 配列の領域の解放をおこなう。

また、2次元配列のイメージ図は次のとおりです。

2次元配列のイメージ図

この図の場合は、data[0][0]からdata[2][2]までの領域を動的確保した……と考えてください。

領域の動的確保をしているのは、次の部分です。

  data = (int**)malloc(sizeof(int*)*n1); //int*型のスペースを要素数([☆][])の分だけ確保する。
  for(e1=0; e1<n1; e1=e1+1)
  {
    data[e1] = (int*)malloc(sizeof(int)*n2); //int型のスペースを要素数([][☆])の分だけ確保する。
  }

この部分では、data(2重ポインター)に「n1個(入力値1)の要素数のint*型(ポインター)領域」を確保させています。こうすることで、data[e1]というように、1次元配列化することができます。

そして、data[e1](ポインター)に「n2個(入力値2)の要素数のint型領域」を確保させています。こうすることで、data[e1][e2]というように、2次元配列化することができます。

簡単に言えば、「配列の中に配列を作ってしまおう」ということですね。

ただし、malloc関数で動的確保した領域は、free関数で最後に解放してあげる必要があります。

  for(e1=0; e1<n1; e1=e1+1)
  {
    free(data[e1]); //メモリー領域([][☆])の解放
  }
  free(data); //メモリー領域([☆][])の解放

これで、data[e1]とdataが確保していた領域を解放することができます。

それでは、このプログラムの実行結果の例を見ていきましょう。

実行結果の例

配列の要素数(data[☆][])を入力してください。>3
配列の要素数(data[][☆])を入力してください。>3
整数値を入力してください。
data[0][0]の入力>1
data[0][1]の入力>2
data[0][2]の入力>3
data[1][0]の入力>4
data[1][1]の入力>5
data[1][2]の入力>6
data[2][0]の入力>7
data[2][1]の入力>8
data[2][2]の入力>9
配列に格納された整数値は、以下のとおりです。
data[0][0] = 1
data[0][1] = 2
data[0][2] = 3
data[1][0] = 4
data[1][1] = 5
data[1][2] = 6
data[2][0] = 7
data[2][1] = 8
data[2][2] = 9

入力した整数値が、正しく表示されていることがわかります!

3次元配列の領域の動的確保をするプログラム例

ソースコード

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
  int ***data; //3重ポインター変数(後で3次元配列化する。)
  int n1; //3次元配列の要素数([☆][][])
  int n2; //3次元配列の要素数([][☆][])
  int n3; //3次元配列の要素数([][][☆])
  int e1; //3次元配列の要素番号([☆][][])
  int e2; //3次元配列の要素番号([][☆][])
  int e3; //3次元配列の要素番号([][][☆])

  printf("配列の要素数(data[☆][][])を入力してください。>");
  scanf("%d",&n1);
  printf("配列の要素数(data[][☆][])を入力してください。>");
  scanf("%d",&n2);
  printf("配列の要素数(data[][][☆])を入力してください。>");
  scanf("%d",&n3);

  //malloc関数で、メモリー領域を動的確保する。
  data = (int***)malloc(sizeof(int**)*n1); //int**型のスペースを要素数([☆][][])の分だけ確保する。
  for(e1=0; e1<n1; e1=e1+1)
  {
    data[e1] = (int**)malloc(sizeof(int*)*n2); //int*型のスペースを要素数([][☆][])の分だけ確保する。
    for(e2=0; e2<n2; e2=e2+1)
    {
      data[e1][e2] = (int*)malloc(sizeof(int)*n3); //int型のスペースを要素数([][][☆])の分だけ確保する。
    }
  }

  printf("整数値を入力してください。\n");
  for(e1=0; e1<n1; e1=e1+1) //要素数([☆][][])の分だけループする。
  {
    for(e2=0; e2<n2; e2=e2+1) //要素数([][☆][])の分だけループする。
    {
      for(e3=0; e3<n3; e3=e3+1) //要素数([][][☆])の分だけループする。
      {
        printf("data[%d][%d][%d]の入力>",e1,e2,e3);
        scanf("%d",&data[e1][e2][e3]); //整数値の入力
      }
    }
  }

  printf("配列に格納された整数値は、以下のとおりです。\n");
  for(e1=0; e1<n1; e1=e1+1) //要素数([☆][][])の分だけループする。
  {
    for(e2=0; e2<n2; e2=e2+1) //要素数([][☆][])の分だけループする。
    {
      for(e3=0; e3<n3; e3=e3+1) //要素数([][][☆])の分だけループする。
      {
        printf("data[%d][%d][%d] = %d\n",e1,e2,e3,data[e1][e2][e3]); //整数値の出力
      }
    }
  }

  //メモリーの解放は、malloc関数のときの逆の順番でおこなう。
  for(e1=0; e1<n1; e1=e1+1)
  {
    for(e2=0; e2<n2; e2=e2+1)
    {
      free(data[e1][e2]); //メモリー領域([][][☆])の解放
    }
    free(data[e1]); //メモリー領域([][☆][])の解放
  }
  free(data); //メモリー領域([☆][][])の解放

  return(0);
}

ソースコードの説明

3次元配列を作りたいので、***dataというポインター変数を用意し、それを3次元配列化します。 3次元配列なので3重ポインターです。

このプログラムの大まかな流れは、次のとおりです。

プログラムの流れ
  1. ***dataなどの変数を用意する。
  2. 配列の要素数を入力させる。
  3. 入力された要素数の領域を自動で確保し、***dataを3次元配列化する。
  4. 3次元配列dataに整数値を、順番に入力していく。
  5. 3次元配列dataに入れられた整数値を、順番に表示していく。
  6. 配列の領域の解放をおこなう。

また、3次元配列のイメージ図は次のとおりです。

3次元配列のイメージ図(表面)

2次元配列に奥行きが追加されましたね。この図の裏面は次のとおりです。

3次元配列のイメージ図(裏面)

この2つの図の場合は、data[0][0][0]からdata[1][2][2]までの領域を動的確保した……と考えてください。

領域の動的確保をしているのは、次の部分です。

  data = (int***)malloc(sizeof(int**)*n1); //int**型のスペースを要素数([☆][][])の分だけ確保する。
  for(e1=0; e1<n1; e1=e1+1)
  {
    data[e1] = (int**)malloc(sizeof(int*)*n2); //int*型のスペースを要素数([][☆][])の分だけ確保する。
    for(e2=0; e2<n2; e2=e2+1)
    {
      data[e1][e2] = (int*)malloc(sizeof(int)*n3); //int型のスペースを要素数([][][☆])の分だけ確保する。
    }
  }

この部分では、data(3重ポインター)に「n1個(入力値1)の要素数のint**型(2重ポインター)領域」を確保させています。こうすることで、data[e1]というように、1次元配列化することができます。

そして、data[e1](2重ポインター)に「n2個(入力値2)の要素数のint*型(ポインター)領域」を確保させています。こうすることで、data[e1][e2]というように、2次元配列化することができます。

さらに、data[e1][e2](ポインター)に「n3個(入力値3)の要素数のint型領域」を確保させています。こうすることで、data[e1][e2][e3]というように、3次元配列化することができます。

簡単に言えば、「配列の中に配列を作って、さらにその中に配列を作ってしまおう」ということですね。

ただし、malloc関数で動的確保した領域は、free関数で最後に解放してあげる必要があります。

  for(e1=0; e1<n1; e1=e1+1)
  {
    for(e2=0; e2<n2; e2=e2+1)
    {
      free(data[e1][e2]); //メモリー領域([][][☆])の解放
    }
    free(data[e1]); //メモリー領域([][☆][])の解放
  }
  free(data); //メモリー領域([☆][][])の解放

これで、data[e1][e2]とdata[e1]とdataが確保していた領域を解放することができます。

それでは、このプログラムの実行結果の例を見ていきましょう。

実行結果の例

配列の要素数(data[☆][][])を入力してください。>2
配列の要素数(data[][☆][])を入力してください。>3
配列の要素数(data[][][☆])を入力してください。>3
整数値を入力してください。
data[0][0][0]の入力>1
data[0][0][1]の入力>2
data[0][0][2]の入力>3
data[0][1][0]の入力>4
data[0][1][1]の入力>5
data[0][1][2]の入力>6
data[0][2][0]の入力>7
data[0][2][1]の入力>8
data[0][2][2]の入力>9
data[1][0][0]の入力>10
data[1][0][1]の入力>11
data[1][0][2]の入力>12
data[1][1][0]の入力>13
data[1][1][1]の入力>14
data[1][1][2]の入力>15
data[1][2][0]の入力>16
data[1][2][1]の入力>17
data[1][2][2]の入力>18
配列に格納された整数値は、以下のとおりです。
data[0][0][0] = 1
data[0][0][1] = 2
data[0][0][2] = 3
data[0][1][0] = 4
data[0][1][1] = 5
data[0][1][2] = 6
data[0][2][0] = 7
data[0][2][1] = 8
data[0][2][2] = 9
data[1][0][0] = 10
data[1][0][1] = 11
data[1][0][2] = 12
data[1][1][0] = 13
data[1][1][1] = 14
data[1][1][2] = 15
data[1][2][0] = 16
data[1][2][1] = 17
data[1][2][2] = 18

入力した整数値が、正しく表示されていることがわかります!

もうすでにお気づきの方もいらっしゃると思いますが、1次元から2次元、2次元から3次元……と、次元を上げるときには「ある規則性」があります。

ここでは説明しませんが、この規則性に気づいたら、4次元配列バージョンも5次元配列バージョンも作れます。興味のある方は、ぜひ、挑戦してみてください。

今回の内容は難しかったと思います。皆さん、お疲れさまでした。


 

みなためじゃんけん

このコーナーは、私と擬似的にじゃんけんできるコーナーです。

みなためじゃんけん、じゃんけんぽん!

私が出したのは……





パー

パーでした! チョキの勝利です!



この記事をSNSでシェアする

プログラミングカテゴリーの最新記事(5件)

最新記事(10件)

管理人のTwitter

内部リンク集