web-dev-qa-db-ja.com

Cで動的な多次元配列を使用するにはどうすればよいですか?

誰かがCを使用して動的に割り当てられた多次元配列を使用する方法を知っていますか?それは可能ですか?

58
rpf

動的割り当てでは、mallocを使用します。

int** x;

x = malloc(dimension1_max * sizeof(int*));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = malloc(dimension2_max * sizeof(int));
}

[...]

for (int i = 0; i < dimension1_max; i++) {
  free(x[i]);
}
free(x);

これにより、サイズdimension1_max * dimension2_maxの2D配列が割り当てられます。したがって、たとえば、640 * 480配列(画像のfeピクセル)が必要な場合は、dimension1_max = 640、dimension2_max = 480を使用します。その後、x[d1][d2]を使用して配列にアクセスできます。ここで、d1 = 0..639、d2 = 0..479。

しかし、SOまたはGoogleも検索すると、他の可能性が明らかになります。たとえば、 in SO question

この場合、配列はメモリの連続した領域(640 * 480バイト)を割り当てないことに注意してください。したがって、条件を満たす配列を取得するには、上記のmallocブロックを次のように置き換えます。

int** x;
int* temp;

x = malloc(dimension1_max * sizeof(int*));
temp = malloc(dimension1_max * dimension2_max * sizeof(int));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = temp + (i * dimension2_max);
}

[...]

free(temp);
free(x);
74
schnaader

C99以降、Cには動的境界を持つ2D配列があります。そのような獣がスタック上に割り当てられることを回避したい場合(これが必要です)、次のように一度に簡単に割り当てることができます

double (*A)[n] = malloc(sizeof(double[n][n]));

以上です。その後、A[i][j]。そして最後にそれを忘れないでください

free(A);

Randy Meyersは 可変長配列(VLA) を説明する一連の記事を書きました。

73
Jens Gustedt

基礎

Cの配列は、[]演算子を使用して宣言およびアクセスされます。そのため

int ary1[5];

5つの整数の配列を宣言します。要素にはゼロから番号が付けられるため、ary1[0]が最初の要素であり、ary1[4]が最後の要素です。注1:デフォルトの初期化はありません。そのため、配列が占有するメモリには、最初にanythingが含まれている場合があります。注2:ary1[5]は未定義の状態でメモリにアクセスします(アクセスできない場合もあります)。

多次元配列は、配列の配列(...の配列)として実装されます。そう

float ary2[3][5];

それぞれ5つの浮動小数点数の3つの1次元配列の配列を宣言します。 ary2[0][0]は最初の配列の最初の要素、ary2[0][4]は最初の配列の最後の要素、ary2[2][4]は最後の配列の最後の要素です。 '89規格では、このデータは連続している必要があります(私のK&R 2nd。ed。の216ページのA8.6.2項)が、パディングには依存しないようです。

複数の次元で動的になろうとする

コンパイル時に配列のサイズがわからない場合は、動的に配列を割り当てる必要があります。試してみたい

double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */

コンパイラが割り当てを埋めない場合に機能するはずです(1次元配列の間に余分なスペースを挿入します)。一緒に行く方が安全かもしれません:

double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */

しかし、いずれにせよ、トリックは逆参照時に発生します。 bufのタイプが間違っているため、buf[i][j]を記述できません。また使用できません

double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */

コンパイラはhdl4がdoubleのアドレスのアドレスであることを期待しているためです。これはエラーであるため、double incomplete_ary4[][];も使用できません。

だからあなたは何ができますか?

  • 行と列の計算を自分で行う
  • 関数で作業を割り当てて実行する
  • ポインターの配列を使用する(qrdlが話しているメカニズム)

自分で数学をする

次のように、各要素のメモリオフセットを計算するだけです。

  for (i=0; i<3; ++i){
     for(j=0; j<3; ++j){
        buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about 
                                             padding in this case */
     }
  }

関数で作業を割り当てて実行する

引数として必要なサイズを取る関数を定義し、通常どおり続行します

void dary(int x, int y){
  double ary4[x][y];
  ary4[2][3] = 5;
}

もちろん、この場合ary4はローカル変数であり、それを返すことはできません。配列のすべての作業は、itが呼び出す関数で呼び出す関数で実行する必要があります。

ポインターの配列

このことを考慮:

double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
   hdl5[i] = malloc(5*sizeof(double))
   /* Error checking */
}

hdl5は、それぞれがdoubleの配列を指すポインターの配列を指します。クールな点は、2次元配列表記を使用してこの構造にアクセスできることです---hdl5[0][2]は最初の行の中央の要素を取得します---しかし、これは別の種類のオブジェクトですdouble ary[3][5];で宣言された2次元配列よりも。

この構造は2次元配列よりも柔軟性があります(行の長さが同じである必要はないため)が、一般にアクセスに時間がかかり、より多くのメモリが必要になります(中間ポインターを保持する場所が必要です)。

私はガードを設定していないので、すべての配列のサイズを自分で追跡する必要があることに注意してください。

算術

cは、ベクトル、行列、テンソルの計算をサポートしていません。自分で実装するか、ライブラリを取り込む必要があります。

スケーラーによる乗算、および同じランクの配列の加算と減算は簡単です。要素をループして、操作を実行するだけです。内部製品も同様に簡単です。

外部製品はより多くのループを意味します。

51
dmckee

コンパイル時の列数がわかっている場合、それは非常に簡単です。

#define COLS ...
...
size_t rows;
// get number of rows
T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T

apを2D配列のように扱うことができます:

ap[i][j] = x;

完了したら、次のように割り当てを解除します

free(ap);

コンパイル時の列数がわからないが、可変長配列をサポートするC99コンパイラーまたはC2011コンパイラーで作業している場合、それは非常に簡単です。

size_t rows;
size_t cols;
// get rows and cols
T (*ap)[cols] = malloc(sizeof *ap * rows);
...
ap[i][j] = x;
...
free(ap);

コンパイル時に列数がわからず、可変長配列をサポートしていないバージョンのCで作業している場合は、別のことをする必要があります。すべての要素を連続したチャンク(通常の配列など)に割り当てる必要がある場合、メモリを1D配列として割り当て、1Dオフセットを計算できます。

size_t rows, cols;
// get rows and columns
T *ap = malloc(sizeof *ap * rows * cols);
...
ap[i * rows + j] = x;
...
free(ap);

メモリが連続している必要がない場合は、2段階の割り当て方法を使用できます。

size_t rows, cols;
// get rows and cols
T **ap = malloc(sizeof *ap * rows);
if (ap)
{
  size_t i = 0;
  for (i = 0; i < cols; i++)
  {
    ap[i] = malloc(sizeof *ap[i] * cols);
  }
}

ap[i][j] = x;

割り当ては2段階のプロセスであったため、割り当て解除も2段階のプロセスである必要があります。

for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);
9
John Bode

以下は、各次元でmake_3d_arrayN1、およびN2要素を使用して多次元3D配列を割り当てるサブルーチンN3を定義し、乱数を設定する作業コードです。 A[i][j][k]という表記を使用して、その要素にアクセスできます。

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


// Method to allocate a 2D array of floats
float*** make_3d_array(int nx, int ny, int nz) {
    float*** arr;
    int i,j;

    arr = (float ***) malloc(nx*sizeof(float**));

    for (i = 0; i < nx; i++) {
        arr[i] = (float **) malloc(ny*sizeof(float*));

        for(j = 0; j < ny; j++) {
            arr[i][j] = (float *) malloc(nz * sizeof(float));
        }
    }

    return arr;
} 



int main(int argc, char *argv[])
{
    int i, j, k;
    size_t N1=10,N2=20,N3=5;

    // allocates 3D array
    float ***ran = make_3d_array(N1, N2, N3);

    // initialize pseudo-random number generator
    srand(time(NULL)); 

    // populates the array with random numbers
    for (i = 0; i < N1; i++){
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                ran[i][j][k] = ((float)Rand()/(float)(Rand_MAX));
            }
        }
   }

    // prints values
    for (i=0; i<N1; i++) {
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                printf("A[%d][%d][%d] = %f \n", i,j,k,ran[i][j][k]);
            }
        }
    }

    free(ran);
}
0
Rodrigo

mallocが行います。

 int rows = 20;
 int cols = 20;
 int *array;

  array = malloc(rows * cols * sizeof(int));

ヘルプについては、以下の記事を参照してください:

http://courses.cs.vt.edu/~cs2704/spring00/mcquain/Notes/4up/Managing2DArrays.pdf

0
Rahul Tripathi