動的な二次元配列を実行時に確保
実行時に大きさが決まる配列をC言語で書いてみます。これは動的な二次元配列の確保と表現されます。calloc
関数を使います。
calloc関数
Cell Allocation(メモリセルの配置)関数です。空きメモリから必要なメモリブロックを確保します。こちらなどに情報があります。
実行時に確保と開放を行うサンプルプログラム(一次元配列)
一次元配列はよくあるパターンです。メモリブロックを確保して,その先頭アドレスをポインタ変数に代入します。calloc
は確保したメモリブロックの先頭アドレスを返すだけで,何に使われるか分からないので,(int *)
としてキャストしています。int型の要素が代入される配列を指すポインタ(先頭アドレス)として,a
に代入しています。使用し終わったらfree
関数を使ってメモリブロックを開放しています。
#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;
}
実行時に確保と開放を行うサンプルプログラム(二次元配列)
int
型の要素がh
個代入できる大きさの一次元配列をv
個用意しています。使用し終わったらfree
関数を使ってメモリブロックを開放しています。開放する順番は逆順になりますので,注意してください。calloc
は確保したメモリブロックの先頭アドレスを返すだけで,何に使われるか分からないので,int **
やint *
としてキャストしています。なお,int **a
とint** a
は同じです。前者の方が古い書き方です。
#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;
}
上の図で,-456は間違いです。16074が正しくなります。
配列の確保と開放を行う専用関数を呼ぶサンプルプログラム
main
関数でmakeArray
関数とfreeArray
関数を呼び出しています。その際の引数ですが,ポインタを指すポインタのアドレス&a
・・・つまりポインタのポインタを「指すもの」なので,ポインタのポインタのポインタとなり,引数を受け取る側では***
となります。そしてa
ですが,上のサンプルプログラムでは,一次元配列(長さh
個)の先頭アドレスをv
個だけ格納する一次元配列の先頭アドレスそのものでした。今回は,さらにその先頭を指すアドレスが格納されているのが'a'となりますので,そのa
に代入されているアドレス先にある実態を表現する*a
に割りつけます。つまり*a = (int**)calloc(v, sizeof(int*));
と書きます。for
文内でのメモリの割り当てですが,*a[i]
と書きたくなりますが,*
より[
の方が結びつきの方が強いので,*(a[i])
と同じになってしまい,エラーになります。そのため (*a)[i]
と書きます。
#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
関数を書いてみます。上の二つのサンプルプログラムの応用になります。これは後述のように矢印演算子(アロー演算子)を使った方が綺麗に書けます。
#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;
}
矢印演算子を使って書くと次のようになります。
#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]の様にして各要素にわかりやすくアクセスできます。
#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;
}
上のプログラムで,メモリ割り当てと開放を関数化するプログラムをもうすぐ掲載