it-swarm.com.de

Wie arbeite ich mit dynamischen mehrdimensionalen Arrays in C?

Weiß jemand, wie ich dynamisch zugewiesene mehrdimensionale Arrays mit C verwenden kann? Ist das möglich?

57
rpf

Bei dynamischer Zuordnung mit 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);

Dies weist ein 2D-Array der Größe dimension1_max * dimension2_max zu. Wenn Sie beispielsweise ein 640 * 480-Array (z. B. Pixel eines Bildes) benötigen, verwenden Sie dimension1_max = 640, dimension2_max = 480. Sie können dann mit x[d1][d2] auf das Array zugreifen, wobei d1 = 0..639, d2 = 0 ist. .479.

Bei einer Suche nach SO oder Google ergeben sich jedoch auch andere Möglichkeiten, beispielsweise in dieser SO - Frage

Beachten Sie, dass Ihr Array in diesem Fall keinen zusammenhängenden Speicherbereich (640 * 480 Byte) zuordnet, was zu Problemen mit Funktionen führen kann, die dies annehmen. Damit das Array die Bedingung erfüllt, ersetzen Sie den obigen Malloc-Block durch Folgendes:

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);
75
schnaader

Seit C99, 13 Jahren, verfügt C über 2D-Arrays mit dynamischen Grenzen. Wenn Sie vermeiden möchten, dass solche Tiere auf dem Stapel zugewiesen werden (was Sie sollten), können Sie sie einfach wie folgt in einem Arbeitsgang zuordnen

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

und das ist es. Sie können es dann einfach verwenden, da Sie für 2D-Arrays mit etwas wie A[i][j] verwendet werden. Und vergiss das am Ende nicht

free(A);

Randy Meyers schrieb eine Reihe von Artikeln, in denen Arrays mit variabler Länge (VLAs) erläutert werden.

72
Jens Gustedt

Grundlagen

Arrays in c werden mit dem Operator [] deklariert und abgerufen. Damit

int ary1[5];

deklariert ein Array von 5 Ganzzahlen. Elemente sind von Null aus nummeriert, also ist ary1[0] das erste Element und ary1[4] das letzte Element. Hinweis1: Es gibt keine Standardinitialisierung, daher enthält der vom Array belegte Arbeitsspeicher anfangs irgendwas. Hinweis2: ary1[5] greift auf den Speicher in einem undefinierten Status zu (auf den Sie möglicherweise nicht einmal zugreifen können), tun Sie es also nicht!

Mehrdimensionale Arrays werden als Array von Arrays (von Arrays (von ...)) implementiert. So

float ary2[3][5];

deklariert ein Array von 3 eindimensionalen Arrays mit jeweils 5 Fließkommazahlen. Nun ist ary2[0][0] das erste Element des ersten Arrays, ary2[0][4] das letzte Element des ersten Arrays und ary2[2][4] das letzte Element des letzten Arrays. Der '89-Standard verlangt, dass diese Daten zusammenhängend sind (Abschnitt A8.6.2 auf Seite 216 meiner 2. Auflage von K & R), scheint jedoch beim Padding agnostisch zu sein.

Versuchen, in mehr als einer Dimension dynamisch zu werden

Wenn Sie die Größe des Arrays zur Kompilierzeit nicht kennen, sollten Sie das Array dynamisch zuweisen. Es ist verlockend, es zu versuchen

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

was funktionieren sollte, wenn der Compiler die Zuordnung nicht auffüllt (halten Sie zusätzlichen Abstand zwischen den eindimensionalen Arrays). Es könnte sicherer sein mit:

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

in jedem Fall kommt der Trick jedoch zur Dereferenzierung. Sie können buf[i][j] nicht schreiben, da buf den falschen Typ hat. Sie können auch nicht verwenden

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

weil der Compiler erwartet, dass hdl4 die Adresse einer Adresse eines Double ist. Sie können auch double incomplete_ary4[][]; nicht verwenden, da dies ein Fehler ist.

Also, was kannst du machen?

  • Führen Sie die Zeilen- und Spaltenberechnung selbst aus
  • Ordnen Sie zu und erledigen Sie die Arbeit in einer Funktion
  • Verwenden Sie ein Array von Zeigern (der Mechanismus, über den qrdl spricht)

Rechnen Sie selbst

Berechnen Sie einfach den Speicherversatz für jedes Element wie folgt:

  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 */
     }
  }

Ordnen Sie zu und erledigen Sie die Arbeit in einer Funktion

Definieren Sie eine Funktion, die die benötigte Größe als Argument verwendet, und fahren Sie wie gewohnt fort

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

In diesem Fall ist ary4 natürlich eine lokale Variable, die Sie nicht zurückgeben können: Die gesamte Arbeit mit dem Array muss in der Funktion ausgeführt werden, die Sie in Funktionen aufrufen, die von it aufgerufen werden.

Ein Array von Zeigern

Bedenken Sie:

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

Jetzt zeigt hdl5 auf ein Array von Zeigern, von denen jeder auf ein Array von Doubles zeigt. Das coole Bit ist, dass Sie die zweidimensionale Array-Schreibweise verwenden können, um auf diese Struktur zuzugreifen. ---hdl5[0][2] ruft das mittlere Element der ersten Zeile auf. dimensionales Array, das von double ary[3][5]; deklariert wurde.

Diese Struktur ist flexibler als ein zweidimensionales Array (weil die Zeilen nicht die gleiche Länge haben müssen), aber der Zugriff auf sie ist im Allgemeinen langsamer und erfordert mehr Speicher (Sie benötigen einen Platz für die Zwischenzeiger).

Beachten Sie, dass Sie, da ich keine Wachen eingerichtet habe, die Größe aller Arrays selbst verfolgen müssen. 

Arithmetik

c bietet keine Unterstützung für Vektor-, Matrix- oder Tensor-Mathematik, Sie müssen sie selbst implementieren oder eine Bibliothek mitbringen.

Die Multiplikation mit einem Scaler sowie das Hinzufügen und das Subtrahieren von Arrays desselben Ranges sind einfach: Führen Sie einfach die Elemente durch und führen Sie die Operation aus. Innere Produkte sind ähnlich unkompliziert.

Äußere Produkte bedeuten mehr Schleifen.

50
dmckee

Wenn Sie die Anzahl der Spalten zur Kompilierzeit kennen, ist das ganz einfach:

#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

Sie können ap wie jedes 2D-Array behandeln:

ap[i][j] = x;

Wenn Sie fertig sind, teilen Sie es auf

free(ap);

Wenn Sie die Anzahl der Spalten zur Kompilierzeit nicht kennen, aber mit einem C99-Compiler oder einem C2011-Compiler arbeiten, der Arrays mit variabler Länge unterstützt, ist es dennoch ziemlich einfach:

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

Wenn Sie die Anzahl der Spalten zum Zeitpunkt des Kompilierens nicht kennen und mit einer Version von C arbeiten, die keine Arrays mit variabler Länge unterstützt, müssen Sie etwas anderes tun. Wenn Sie alle Elemente in einem zusammenhängenden Block (wie ein reguläres Array) zuordnen möchten, können Sie den Speicher als 1D-Array zuordnen und einen 1D-Offset berechnen:

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

Wenn der Speicher nicht zusammenhängend sein muss, können Sie eine Zuweisungsmethode in zwei Schritten verwenden:

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;

Da die Zuweisung ein zweistufiger Prozess war, muss die Freigabe auch ein zweistufiger Prozess sein:

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

Hier ist ein Arbeitscode, der eine Unterroutine make_3d_array definiert, um ein mehrdimensionales 3D-Array mit N1-, N2- und N3-Elementen in jeder Dimension zuzuweisen und es dann mit Zufallszahlen aufzufüllen. Sie können die Notation A[i][j][k] verwenden, um auf die Elemente zuzugreifen.

#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 wird es tun.

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

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

Hilfe finden Sie im folgenden Artikel: -

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

0
Rahul Tripathi