Wie arbeite ich mit dynamischen mehrdimensionalen Arrays in C?

Lesezeit: 14 Minuten

Benutzeravatar von rpf
rpf

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

Benutzeravatar von Jens Gustedt
Jens Gustedt

Seit C99 hat C 2D-Arrays mit dynamischen Grenzen. Wenn Sie vermeiden möchten, dass solche Bestien auf dem Stapel zugewiesen werden (was Sie sollten), können Sie sie wie folgt einfach auf einmal zuweisen

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

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

free(A);

Randy Meyers schrieb eine Reihe von erklärenden Artikeln Arrays mit variabler Länge (VLAs).

  • Teile 2, 3und 4 der Meyers-Reihe sind auch bei Dr. Dobbs erhältlich.

    – Phil Müller

    1. November 2013 um 21:19 Uhr

  • Obwohl dies eine schöne und elegante Lösung ist, liegt die Schönheit mehr in der Deklaration A als in der tatsächlichen Zuordnung; insbesondere wird nirgendwo ein 2-D-Array zugewiesen, geschweige denn eins mit dynamischer Größe. Der Malloc weist einfach einen linearen Abschnitt untypisierten Speichers zu, der für irgendetwas geeignet ausgerichtet ist; die Adresse A mit initialisiert wird, könnte aus jeder Quelle stammen (na ja, wegen Aliasing-Problemen würde ich annehmen, dass es aus einem eindimensionalen Array von char oder double oder einem “echten” zweidimensionalen Array stammen könnte). Die Laufzeit sizeof berechnet nur eine Zahl (natürlich immer die richtige) ;-).

    – Peter – Setzen Sie Monica wieder ein

    15. Februar 2016 um 15:51 Uhr

  • Gute Antwort! Ergänzung: In Ihrem Beispiel haben beide Dimensionen die gleiche Größe n. Bei unterschiedlichen Größen sieht das so aus: double (*a)[y] = malloc(sizeof(double[x][y]));. Mit drei Dimensionen: double (*a)[y][z] = malloc(sizeof(double[x][y][z]));. Usw.

    – jcsahnwaldt Wiedereinsetzung von Monica

    8. Oktober 2017 um 10:20 Uhr

  • Die einzig gute Antwort auf diese Frage. Ich würde es jedoch immer vorziehen, die Zeigervariable in der zu verwenden sizeof() so was: double (*A)[n] = malloc(n*sizeof(*A)); Der Vorteil ist, dass Sie die Typangaben in der nicht falsch machen können sizeof()und es ist immer die gleiche Form von arrayLength*sizeof(*newPointerVar) als die malloc() Streit. Ich glaube, diese Form hat die geringste Möglichkeit, dass sich Fehler einschleichen.

    – Cmaster – Wiedereinsetzung von Monica

    17. Januar 2020 um 12:18 Uhr

Benutzeravatar von schnaader
schnaader

Bei dynamischer Zuweisung mit malloc:

int** x;

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

//Writing values
x[0..(dimension1_max-1)][0..(dimension2_max-1)] = Value; 
[...]

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

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

Aber eine Suche auf SO oder Google offenbart auch andere Möglichkeiten, zum Beispiel in dieser SO-Frage

Beachten Sie, dass Ihr Array in diesem Fall keinen zusammenhängenden Speicherbereich (640 * 480 Bytes) zuweist, was zu Problemen mit Funktionen führen könnte, die dies annehmen. Damit das Array also die Bedingung erfüllt, ersetzen Sie den malloc-Block oben durch diesen:

int** x;
int* temp;

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

[...]

free(temp);
free(x);

  • Dies wird nicht kompiliert, Sie müssen x als “int **” deklarieren, nicht als “int[][]”.

    – Adam Rosenfield

    27. Mai 2009 um 20:28 Uhr

  • Was macht das genau? Was ist dimension1_max und dimension2_max? Was bedeutet das erste „für“?

    – rpf

    27. Mai 2009 um 20:35 Uhr

  • Danke, Adam, ich wusste, dass etwas nicht stimmte, obwohl ich bereits die erste Größe von int* anstelle von int korrigiert hatte.

    – schnaader

    27. Mai 2009 um 20:39 Uhr

  • Es ist kein mehrdimensionales Array – es ist ein Array von Zeigern auf int oder ein Array von Arrays. Um Speicher für ein echtes 2D-Array zuzuweisen, müssen Sie malloc (dim1 * dim2 * sizeof (int)) verwenden. Wenn eine Funktion einen Zeiger auf ein 2D-Array erwartet, wie foo(int * bar[5][6]) und Sie passieren Ihr x, passieren seltsame Dinge. Sehen en.wikipedia.org/wiki/C_syntax#Multidimensional_arrays

    – qrdl

    28. Mai 2009 um 6:45 Uhr

  • Das macht nicht Weisen Sie ein 2D-Array zu, aber ein Array von Zeigern plus Arrays, auf die es zeigt. Die Adressierung ist sehr unterschiedlich, sie hat einen Speicher-Overhead und ist wahrscheinlich weniger effizient.

    – zu ehrlich für diese Seite

    15. Februar 2016 um 14:20 Uhr

dmckee --- Benutzeravatar des Ex-Moderators Kitten
dmckee — Ex-Moderator-Kätzchen

Grundlagen

Arrays in c werden mit deklariert und aufgerufen [] Operator. So dass

int ary1[5];

deklariert ein Array von 5 ganzen Zahlen. Elemente werden also von Null an nummeriert ary1[0] ist das erste Element, und ary1[4] ist das letzte Element. Hinweis 1: Es gibt keine Standardinitialisierung, daher kann der vom Array belegte Speicher zunächst enthalten irgendetwas. Anmerkung 2: ary1[5] greift auf Speicher in einem undefinierten Zustand zu (auf den Sie möglicherweise nicht einmal zugreifen können), also tun Sie es nicht!

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

float ary2[3][5];

deklariert ein Array aus 3 eindimensionalen Arrays mit jeweils 5 Gleitkommazahlen. Jetzt ary2[0][0] ist das erste Element des ersten Arrays, ary2[0][4] ist das letzte Element des ersten Arrays und ary2[2][4] ist das letzte Element des letzten Arrays. Der 89er-Standard erfordert, dass diese Daten zusammenhängend sind (Abschnitt A8.6.2 auf Seite 216 meines K&R, 2. Aufl.), scheint aber beim Auffüllen agnostisch zu sein.

Der Versuch, 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 (zusätzlichen Platz zwischen den eindimensionalen Arrays kleben). Es könnte sicherer sein zu gehen mit:

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

aber so oder so kommt der Trick zum Zeitpunkt der Dereferenzierung. Du kannst nicht schreiben buf[i][j] Weil buf hat den falschen Typ. Kannst du auch nicht verwenden

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

weil der Compiler erwartet hdl4 die Adresse einer Adresse eines Doubles sein. Kannst du auch nicht verwenden double incomplete_ary4[][]; weil dies ein Fehler ist;

Also, was kannst du machen?

  • Führen Sie die Zeilen- und Spaltenarithmetik selbst durch
  • Weisen Sie die Arbeit in einer Funktion zu und erledigen Sie sie
  • 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 */
     }
  }

Weisen Sie die Arbeit in einer Funktion zu und erledigen Sie sie

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

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

In diesem Fall natürlich ary4 ist eine lokale Variable und Sie können sie nicht zurückgeben: Die gesamte Arbeit mit dem Array muss in der Funktion erledigt werden, die Sie in den Funktionen aufrufen es Anrufe.

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 hdl5 zeigt auf ein Array von Zeigern, von denen jeder auf ein Array von Doubles zeigt. Das Coole ist, dass Sie die zweidimensionale Array-Notation verwenden können, um auf diese Struktur zuzugreifen —hdl5[0][2] erhält das mittlere Element der ersten Zeile – aber dies ist nichtsdestoweniger eine andere Art von Objekt als ein zweidimensionales Array, das von deklariert wird double ary[3][5];.

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

Beachten Sie, dass Sie die Größe aller Arrays selbst im Auge behalten müssen, da ich keine Wächter eingerichtet habe.

Arithmetik

c bietet keine Unterstützung für Vektor-, Matrizen- oder Tensormathematik, Sie müssen es selbst implementieren oder eine Bibliothek einbringen.

Die Multiplikation mit einem Scaler sowie die Addition und Subtraktion von Arrays mit demselben Rang sind einfach: Schleifen Sie einfach die Elemente durch und führen Sie die Operation aus, während Sie fortfahren. Innere Produkte sind ähnlich geradlinig.

Außenprodukte bedeuten mehr Schleifen.

  • Nur ein Punkt – ein Array von Arrays ist ein Array von Zeigern, kein echtes mehrdimensionales Array. en.wikipedia.org/wiki/C_syntax#Multidimensional_arrays

    – qrdl

    28. Mai 2009 um 6:50 Uhr

  • @qrdl: Wie ich den Standard int **a1 lese; und int *a2[i]; int a3[n][m]; sind verschiedene Viecher mit unterschiedlicher Semantik. Dem letzten wird ein zusammenhängender (möglicherweise aufgefüllter) Speicherblock ohne einen separaten Satz von Zwischenzeigern zugewiesen … Versuchen Sie es mit printf(“%d\n”,sizeof(int[3][5])/sizeof(int));

    – dmckee — Ex-Moderator-Kätzchen

    28. Mai 2009 um 14:53 Uhr

  • Funktioniert “in einer Funktion zuweisen”? Wie würde der Compiler wissen, wie viel Speicher im Stapel für das Array ary4 zugewiesen werden soll, wenn x und y unbekannt sind?

    – Holzstok

    8. Januar 2015 um 18:59 Uhr

  • @Mikhail Er … x und y sind zur Laufzeit bekannt, und der Compiler ist nicht darauf beschränkt, den Stapelrahmen auf einmal zu skalieren. Versuch es. Natürlich können Sie dieses Array nicht zurückgeben, Sie müssen die ganze Arbeit in der Funktion erledigen, daher ist mein Text etwas irreführend. Ich werde es bearbeiten, um das zu beheben.

    – dmckee — Ex-Moderator-Kätzchen

    9. Januar 2015 um 0:59 Uhr


  • @MIkhail Für das, was es wert ist, denke ich, der Compiler tut müssen die Größe von global gültigen Arrays zur Kompilierzeit kennen.

    – dmckee — Ex-Moderator-Kätzchen

    9. Januar 2015 um 2:01 Uhr

Wenn Sie die Anzahl der Spalten zur Kompilierzeit kennen, ist es ziemlich 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 behandeln ap wie jedes 2D-Array:

ap[i][j] = x;

Wenn Sie fertig sind, heben Sie die Zuordnung auf als 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 immer noch 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 zur Kompilierzeit 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 alle Elemente in einem zusammenhängenden Block (wie einem regulären Array) zugewiesen werden müssen, können Sie den Speicher als 1D-Array zuweisen 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 Sie keinen zusammenhängenden Speicher benötigen, können Sie eine zweistufige Zuweisungsmethode 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 auch die Aufhebung der Zuweisung ein zweistufiger Prozess sein:

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

Benutzeravatar von Rahul Tripathi
Rahul Tripathi

Malloc wird es tun.

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

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

Siehe den folgenden Artikel für Hilfe:-

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

  • Die Frage fragt nach C. Das war C++.

    – Slugonamission

    9. Oktober 2012 um 16:20 Uhr

  • @rahul Ich würde vorschlagen, dass Sie den verlinkten Artikel verlassen, wenn Sie die oben vorgenommene Bearbeitung vornehmen. Obwohl die Syntax spezifisch für C++ ist, würden alle dort eingeführten Konzepte für die Methode gelten, die Sie in Ihrer bearbeiteten Antwort vorschlagen

    – ich bin so verwirrt

    9. Oktober 2012 um 16:26 Uhr

  • @AK4749:- Ich habe es gelöscht, weil ich Angst hatte, meine Punkte zu verlieren. Ich hatte viele von ihnen früher verloren, auch weil die Leute hier sehr spezifisch auf ihre Fragen sind und nicht nachforschen wollen.!!! Wie auch immer, vielen Dank, dass du mich aufgemuntert hast

    – Rahul Tripathi

    9. Oktober 2012 um 16:29 Uhr

  • @RahulTripathi ja, ich habe auch diese “Analness” bei SO bemerkt, aber zu seiner Verteidigung ist es kein Forum, sondern ein Ort, an dem bestimmte Fragen beantwortet werden. Eine andere Atmosphäre, an die man sich gewöhnen muss, denke ich :/

    – ich bin so verwirrt

    9. Oktober 2012 um 16:37 Uhr

Benutzeravatar von Rodrigo
Rodrigo

Hier ist Arbeitscode, der eine Unterroutine definiert make_3d_array um ein mehrdimensionales 3D-Array mit zuzuweisen N1, N2 und N3 Elemente in jeder Dimension und füllt sie dann mit Zufallszahlen. Sie können die Notation verwenden A[i][j][k] um auf seine 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);
}

  • Die Frage fragt nach C. Das war C++.

    – Slugonamission

    9. Oktober 2012 um 16:20 Uhr

  • @rahul Ich würde vorschlagen, dass Sie den verlinkten Artikel verlassen, wenn Sie die oben vorgenommene Bearbeitung vornehmen. Obwohl die Syntax spezifisch für C++ ist, würden alle dort eingeführten Konzepte für die Methode gelten, die Sie in Ihrer bearbeiteten Antwort vorschlagen

    – ich bin so verwirrt

    9. Oktober 2012 um 16:26 Uhr

  • @AK4749:- Ich habe es gelöscht, weil ich Angst hatte, meine Punkte zu verlieren. Ich hatte viele von ihnen früher verloren, auch weil die Leute hier sehr spezifisch auf ihre Fragen sind und nicht nachforschen wollen.!!! Wie auch immer, vielen Dank, dass du mich aufgemuntert hast

    – Rahul Tripathi

    9. Oktober 2012 um 16:29 Uhr

  • @RahulTripathi ja, ich habe auch diese “Analness” bei SO bemerkt, aber zu seiner Verteidigung ist es kein Forum, sondern ein Ort, an dem bestimmte Fragen beantwortet werden. Eine andere Atmosphäre, an die man sich gewöhnen muss, denke ich :/

    – ich bin so verwirrt

    9. Oktober 2012 um 16:37 Uhr

Benutzeravatar von slugonamission
slugonamission

Es gibt keine Möglichkeit, das Ganze auf einmal zuzuweisen. Erstellen Sie stattdessen ein Array von Zeigern und erstellen Sie dann für jeden Zeiger den Speicher dafür. Zum Beispiel:

int** array;
array = (int**)malloc(sizeof(int*) * 50);
for(int i = 0; i < 50; i++)
    array[i] = (int*)malloc(sizeof(int) * 50);

Natürlich können Sie das Array auch als deklarieren int* array[50] und den ersten Malloc überspringen, aber der zweite Satz wird benötigt, um den erforderlichen Speicher dynamisch zuzuweisen.

Es ist möglich, einen Weg zu hacken, um es in einem einzigen Schritt zuzuweisen, aber es würde eine benutzerdefinierte Suchfunktion erfordern, aber das so zu schreiben, dass es immer funktioniert, kann lästig sein. Ein Beispiel könnte sein L(arr,x,y,max_x) arr[(y)*(max_x) + (x)]mallocieren Sie dann einen Block von 50 * 50 Ints oder was auch immer und greifen Sie damit zu L Makro, z

#define L(arr,x,y,max_x) arr[(y)*(max_x) + (x)]

int dim_x = 50;
int dim_y = 50;

int* array = malloc(dim_x*dim_y*sizeof(int));

int foo = L(array, 4, 6, dim_x);

Aber das ist viel schlimmer, es sei denn, Sie kennen die Auswirkungen dessen, was Sie mit dem Präprozessor-Makro machen.

  • falsch, viel zu kompliziert, modernes C kann es besser, siehe meine Antwort.

    – Jens Gustedt

    9. Oktober 2012 um 18:17 Uhr

1414750cookie-checkWie arbeite ich mit dynamischen mehrdimensionalen Arrays in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy