C の多次元配列に関する「私のコードの何が問題なのか」という質問を何十件も見てきました。何らかの理由で、ここで起こっていることに頭を悩ませているように見えないので、他の人への参考としてこの質問に答えることにしました。 :
C で多次元配列を正しく設定、アクセス、および解放するにはどうすればよいですか?
他の人に役立つアドバイスがあれば、遠慮なく投稿してください。
C の多次元配列に関する「私のコードの何が問題なのか」という質問を何十件も見てきました。何らかの理由で、ここで起こっていることに頭を悩ませているように見えないので、他の人への参考としてこの質問に答えることにしました。 :
C で多次元配列を正しく設定、アクセス、および解放するにはどうすればよいですか?
他の人に役立つアドバイスがあれば、遠慮なく投稿してください。
C99以降のCでは、動的な多次元配列でさえ、一度に簡単に割り当てたりmalloc
、解放したりすることができfree
ます。
double (*A)[n] = malloc(sizeof(double[n][n]));
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < n; ++j)
A[i][j] = someinvolvedfunction(i, j);
free(A);
C89 で多次元配列を作成またはシミュレートするには、少なくとも 4 つの異なる方法があります。
1 つは「各行を個別に割り当てる」ことで、Mike の回答で説明されています。これは多次元配列ではなく、多次元配列を模倣しているだけです (特に、要素にアクセスするための構文を模倣しています)。各行のサイズが異なる場合に役立つ可能性があるため、行列を表すのではなく、「ぎざぎざのエッジ」を持つものを表します。
1つは「多次元配列を割り当てる」です。次のようになります。
int (*rows)[NUM_ROWS][NUM_COLS] = malloc(sizeof *rows);
...
free(rows);
次に、要素 [i,j] にアクセスするための構文は(*rows)[i][j]
です。C89 では、NUM_COLS
との両方NUM_ROWS
がコンパイル時に認識されている必要があります。これは真の 2 次元配列であり、rows
それへのポインターです。
1つは「行の配列を割り当てる」です。次のようになります。
int (*rows)[NUM_COLS] = malloc(sizeof(*rows) * NUM_ROWS);
...
free(rows);
次に、要素 [i,j] にアクセスするための構文はrows[i][j]
です。C89 では、NUM_COLS
コンパイル時に認識されている必要があります。これは真の 2 次元配列です。
1つは、「1次元配列を割り当ててふりをする」ことです。次のようになります。
int *matrix = malloc(sizeof(int) * NUM_COLS * NUM_ROWS);
...
free(matrix);
次に、要素 [i,j] にアクセスするための構文はmatrix[NUM_COLS * i + j]
です。これは (もちろん) 真の 2 次元配列ではありません。実際には、1 つと同じレイアウトになります。
静的に言えば、これは簡単に理解できます。
int mtx[3][2] = {{1, 2},
{2, 3},
{3, 4}};
ここでは何も複雑ではありません。3 行 2 列。1 列目のデータ: 1, 2, 3
; 列 2 のデータ: 2, 3, 4
. 同じ構造を介して要素にアクセスできます。
for(i = 0; i<3; i++){
for(j = 0; j<2; j++)
printf("%d ", mtx[i][j]);
printf("\n");
}
//output
//1 2
//2 3
//3 4
これをPointersの観点から見てみましょう。
ブラケットは物事を単純化するのに役立つ非常に優れた構造ですが、動的な環境で作業する必要がある場合には役に立ちません。したがって、これをポインターの観点から考える必要があります。整数の「行」を格納する場合は、配列が必要です。
int row[2] = {1,2};
そして、あなたは何を知っていますか?ポインターのようにこれにアクセスできます。
printf("%d, %d\n",*row,*(row+1)); //prints 1, 2
printf("%d, %d\n",row[0],row[1]); //prints 1, 2
行内の値の数がわからない場合は、int へのポインターがあれば、この配列を動的な長さにすることができ、メモリを与えます。
int *row = malloc(X * sizeof(int)); //allow for X number of ints
*row = 1; //row[0] = 1
*(row+1) = 2; //row[1] = 2
…
*(row+(X-1)) = Y; // row[x-1] = Some value y
これで、動的な 1 次元配列ができました。単一の行。しかし、1 行だけでなく、多数の行が必要であり、その数はわかりません。つまり、別の動的な 1 次元配列が必要です。その配列の各要素は、行を指すポインターになります。
//we want enough memory to point to X number of rows
//each value stored there is a pointer to an integer
int ** matrix = malloc(X * sizeof(int *));
//conceptually:
(ptr to ptr to int) (pointer to int)
**matrix ------------> *row1 --------> [1][2]
*row2 --------> [2][3]
*row3 --------> [3][4]
あとは、これらの動的割り当てを実行するコードを記述するだけです。
int i, j, value = 0;
//allocate memory for the pointers to rows
int ** matrix = malloc(Rows * sizeof(int*));
//each row needs a dynamic number of elements
for(i=0; i<Rows; i++){
// so we need memory for the number of items in each row…
// we could call this number of columns as well
*(matrix + i) = malloc(X * sizeof(int));
//While we’re in here, if we have the items we can populate the matrix
for(j=0; j<X; j++)
*(*(matrix+i)+j) = value; // if you deference (matrix + i) you get the row
// if you add the column and deference again, you
// get the actual item to store (not a pointer!)
}
ここで行うべき最も重要なことの 1 つは、完了したらメモリを解放することです。の各レベルはmalloc()
同じ数のfree()
呼び出しを持つ必要があり、呼び出しは FILO 順 (malloc 呼び出しの逆) である必要があります。
for(i=0; i<Rows; i++)
free(*(matrix + i));
free(matrix);
//set to NULL to clean up, matrix points to allocated memory now so let’s not use it!
matrix = NULL;
typedef された配列を使用する場合は、さらに簡単です。
コードにあるとしましょうtypedef int LabeledAdjMatrix[SIZE][SIZE];
その後、次を使用できます。
LabeledAdjMatrix *pMatrix = malloc(sizeof(LabeledAdjMatrix));
次に、次のように記述できます。
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) (*parr)[i][j] = k++; /* or parr[0][i][j]... */
}
pArr
は行列へのポインタであり、優先度は;*
よりも低いためです。[]
そのため、モードの一般的なイディオムは、行を typedef することです。
typedef int LabeledAdjRow[SIZE];
次に、次のように記述できます。
LabeledAdjRow *pMatrix = malloc(sizeof(LabeledAdjRow) * SIZE);
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) parr[i][j] = k++;
}
memcpy
そして、これらすべてを直接行うことができます:
LabeledAdjRow *pOther = malloc(sizeof(LabeledAdjRow) * SIZE);
memcpy(pOther, pMatrix, sizeof(LabeledAdjRow) * SIZE);