C: Korrektes Freigeben des Speichers eines mehrdimensionalen Arrays

Lesezeit: 7 Minuten

Benutzeravatar von Andreas Grech
Andreas Gretsch

Angenommen, Sie haben den folgenden ANSI-C-Code, der ein mehrdimensionales Array initialisiert:

int main()
{
      int i, m = 5, n = 20;
      int **a = malloc(m * sizeof(int *));

      //Initialize the arrays
      for (i = 0; i < m; i++) { 
          a[i]=malloc(n * sizeof(int));
      }

      //...do something with arrays

      //How do I free the **a ?

      return 0;
}

Nach der Verwendung der **awie befreie ich es korrekt aus dem Speicher?


[Update] (Lösung)

Dank Tims (und der anderen) Antwort kann ich jetzt eine solche Funktion ausführen, um Speicher aus meinem mehrdimensionalen Array freizugeben:

void freeArray(int **a, int m) {
    int i;
    for (i = 0; i < m; ++i) {
        free(a[i]);
    }
    free(a);
}

  • Terminologische Spitzfindigkeit: Dies ist nicht das, was C normalerweise als “mehrdimensionales Array” bezeichnet. Es ist nur die einzige Möglichkeit, die Syntax zu verwenden a[i][j], während beide Dimensionen zur Kompilierzeit noch unbekannt sein können. Die andere Art von mehrdimensionalen Arrays ist ein Array von Arrays anstelle dieses Arrays von Zeigern auf (die ersten Elemente von) Arrays.

    – Steve Jessop

    14. November 2009 um 18:12 Uhr

OK, es gibt ziemlich viel Verwirrung, die genau erklärt, welche Reihenfolge die notwendige ist free() Anrufe müssen eingehen, also werde ich versuchen zu klären, worauf die Leute hinaus wollen und warum.

Beginnen Sie mit den Grundlagen, um Speicherplatz freizugeben, der mit zugewiesen wurde malloc()rufen Sie einfach an free() mit genau dem Zeiger, der Ihnen gegeben wurde malloc(). Also für diesen Code:

int **a = malloc(m * sizeof(int *));

Du brauchst ein passendes:

free(a);

und für diese Zeile:

a[i]=malloc(n * sizeof(int));

Du brauchst ein passendes:

free(a[i]);

innerhalb einer ähnlichen Schleife.

Wo dies kompliziert wird, ist die Reihenfolge, in der dies geschehen muss. Wenn Sie anrufen malloc() mehrmals, um verschiedene Speicherblöcke zu erhalten, im Allgemeinen spielt es keine Rolle, in welcher Reihenfolge Sie aufrufen free() wenn du mit ihnen fertig bist. Die Reihenfolge ist hier jedoch aus einem ganz bestimmten Grund wichtig: Sie verwenden einen Teil davon malloced-Speicher, um die Zeiger auf andere Chunks zu halten mallocEd Speicher. Wegen dir muss
nicht Versuchen Sie, den Speicher zu lesen oder zu schreiben, nachdem Sie ihn zurückgegeben haben
free()bedeutet dies, dass Sie die Chunks mit ihren darin gespeicherten Zeigern freigeben müssen a[i] Vor du befreist die a Stück selbst. Die einzelnen Chunks mit darin gespeicherten Zeigern a[i] sind nicht voneinander abhängig und können es auch sein freed in beliebiger Reihenfolge.

Wenn wir das alles zusammenfassen, erhalten wir Folgendes:

for (i = 0; i < m; i++) { 
  free(a[i]);
}
free(a);

Ein letzter Tipp: Beim Anrufen malloc()erwägen Sie, diese zu ändern:

int **a = malloc(m * sizeof(int *));

a[i]=malloc(n * sizeof(int));

zu:

int **a = malloc(m * sizeof(*a));

a[i]=malloc(n * sizeof(*(a[i])));

Was macht das? Das weiß der Compiler a ist ein int **damit es das feststellen kann sizeof(*a) ist das gleiche wie sizeof(int *). Wenn Sie jedoch später Ihre Meinung ändern und möchten chars oder shorts oder longs oder was auch immer in Ihrem Array statt ints, oder Sie passen diesen Code für die spätere Verwendung in etwas anderem an, müssen Sie nur den einen verbleibenden Verweis auf ändern int in der ersten zitierten Zeile oben, und alles andere ergibt sich automatisch für Sie. Dies beseitigt die Wahrscheinlichkeit von unbemerkten Fehlern in der Zukunft.

Viel Glück!

  • +1 Ausgezeichnete Antwort; Vielen Dank für die Erklärung des Problems mit der “umgekehrten Reihenfolge” und auch des Punkts über das Tun sizeof(*a)

    – Andreas Gretsch

    14. November 2009 um 11:08 Uhr

  • Korrigieren Sie mich bitte auch, wenn ich falsch liege, aber würde ich das richtig sagen? sizeof(*a[i]) entspricht Ihrem sizeof(*(a[i]))da die Array-Notation [] haben einen höheren Vorrang als * ?

    – Andreas Gretsch

    14. November 2009 um 11:14 Uhr

  • Nein, ich denke du hast recht. de.wikipedia.org/wiki/… Ich arbeite jedoch an einer Faustregel, dass, wenn ich es nachschlagen muss, andere, die meinen Code lesen, es wahrscheinlich auch nachschlagen müssten. Um ihnen (und mir später) Ärger zu ersparen, hilft die explizite Verwendung von Klammern bei der Klärung Dinge und sparen Sie Zeit. Das ist aber nur eine persönliche Vorliebe.

    – Tim

    14. November 2009 um 11:19 Uhr

Machen Sie genau das rückgängig, was Sie zugewiesen haben:

  for (i = 0; i < m; i++) { 
      free(a[i]);
  }
  free(a);

Beachten Sie, dass Sie dies im tun müssen umkehren Reihenfolge, aus der Sie den Speicher ursprünglich zugewiesen haben. Wenn du. .. getan hast free(a) Zuerst, dann a[i] würde auf den Speicher zugreifen, nachdem er freigegeben wurde, was ein undefiniertes Verhalten ist.

  • Zu sagen, dass Sie in umgekehrter Reihenfolge freigeben müssen, könnte irreführend sein. Sie müssen nur das Array von Zeigern nach den Zeigern selbst freigeben.

    – Andomar

    14. November 2009 um 10:22 Uhr

  • Ist das nicht eine andere Art, umgekehrt zu sagen?

    – GManNickG

    14. November 2009 um 10:35 Uhr

  • Ich denke, @Andomar bedeutet, dass es egal ist, in welcher Reihenfolge Sie das a freigeben[i]ist drin, nur dass du sie alle befreien musst, bevor du a befreien kannst. Mit anderen Worten, Sie können a freigeben[0] durch ein[m-1] oder ein[m-1] durch ein[0] oder alle sogar a[]gefolgt von den Quoten. Aber ich bin mir auch sicher, dass @GregH es nicht getan hat bedeuten du musstest a machen[]’s in umgekehrter Reihenfolge, insbesondere angesichts seines Codes.

    – paxdiablo

    14. November 2009 um 10:51 Uhr

Benutzeravatar von Arkaitz Jimenez
Arkaitz Jimenez

Sie müssen das Array erneut durchlaufen und so viele Freigaben wie Mallocs für den Speicher, auf den verwiesen wird, ausführen und dann das Array von Zeigern freigeben.

for (i = 0; i < m; i++) { 
      free (a[i]);
}
free (a);

Benutzeravatar von P Shved
P Schwed

Schreiben Sie Ihre Zuweisungsoperatoren in genau umgekehrter Reihenfolge, indem Sie die Funktionsnamen ändern, und alles ist in Ordnung.

  //Free the arrays
  for (i = m-1; i >= 0; i--) { 
      free(a[i]);
  }

  free(a);

Natürlich nicht müssen, zu … haben in der gleichen umgekehrten Reihenfolge aufheben. Sie müssen nur den gleichen Speicher genau einmal freigeben und dürfen die Zeiger auf den zugewiesenen Speicher nicht “vergessen” (wie es gewesen wäre, wenn Sie die a Erste). Aber das Aufheben der Zuordnung in umgekehrter Reihenfolge ist eine gute Faustformel, um letzteres anzugehen.

Wie von litb in den Kommentaren angegeben, wenn die Zuweisung/Aufhebung Nebenwirkungen hatte (wie z new/delete Operatoren in C++), wäre manchmal die Rückwärtsreihenfolge der Freigabe wichtiger als in diesem speziellen Beispiel.

Ich würde malloc() und free() nur einmal aufrufen:

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

int main(void){
  int i, m = 5, n = 20;
  int **a = malloc( m*(sizeof(int*) + n*sizeof(int)) );

  //Initialize the arrays
  for( a[0]=(int*)a+m, i=1; i<m; i++ ) a[i]=a[i-1]+n;

  //...do something with arrays

  //How do I free the **a ?
  free(a);

  return 0;
}

  • wie ist das eine Antwort auf die Frage?

    – Blind

    14. November 2009 um 10:49 Uhr

  • Pavel Shved hat die richtige Antwort geschrieben. Ich habe gerade einen Kommentar mit etwas Code geschrieben.

    – Sambowry

    14. November 2009 um 11:00 Uhr

  • Sie sollten Kommentare in das “Kommentar”-Feld der Frage schreiben. Es unterstützt auch Codeblöcke.

    – Johannes Schaub – litb

    14. November 2009 um 11:03 Uhr

  • @litb: Bitte kopieren Sie meine Antwort in ein Kommentarfeld der Frage. Danke.

    – Sambowry

    14. November 2009 um 12:41 Uhr

  • Es gibt keinen Grund für eine Ablehnung. Ich finde, die Antwort auf viele Fragen zu SO lautet: “Du solltest das nicht tun, tue stattdessen dies”. Es gibt Raum für Antworten auf den Buchstaben der Frage und Antworten auf die Bedeutung der Frage.

    – Jmucchiello

    14. November 2009 um 13:33 Uhr

  • wie ist das eine Antwort auf die Frage?

    – Blind

    14. November 2009 um 10:49 Uhr

  • Pavel Shved hat die richtige Antwort geschrieben. Ich habe gerade einen Kommentar mit etwas Code geschrieben.

    – Sambowry

    14. November 2009 um 11:00 Uhr

  • Sie sollten Kommentare in das “Kommentar”-Feld der Frage schreiben. Es unterstützt auch Codeblöcke.

    – Johannes Schaub – litb

    14. November 2009 um 11:03 Uhr

  • @litb: Bitte kopieren Sie meine Antwort in ein Kommentarfeld der Frage. Danke.

    – Sambowry

    14. November 2009 um 12:41 Uhr

  • Es gibt keinen Grund für eine Ablehnung. Ich finde, die Antwort auf viele Fragen zu SO lautet: “Du solltest das nicht tun, tue stattdessen dies”. Es gibt Raum für Antworten auf den Buchstaben der Frage und Antworten auf die Bedeutung der Frage.

    – Jmucchiello

    14. November 2009 um 13:33 Uhr

1402420cookie-checkC: Korrektes Freigeben des Speichers eines mehrdimensionalen Arrays

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

Privacy policy