Freier Speicher, der einer anderen Funktion zugewiesen wurde?

Lesezeit: 5 Minuten

Benutzeravatar von Jeff Tratner
Jeff Tratner

Ich versuche, C zu lernen, und ich versuche derzeit, eine grundlegende Stack-Datenstruktur zu schreiben, aber ich kann nicht einfach werden malloc/free Rechts.

Hier ist der Code, den ich verwendet habe (ich poste hier nur einen kleinen Teil, um ein bestimmtes Problem zu veranschaulichen, nicht den gesamten Code, aber die Fehlermeldung wurde nur durch Ausführen dieses Beispielcodes generiert valgrind)

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

typedef struct Entry {
    struct Entry *previous;
    int value;
} Entry;

void destroyEntry(Entry entry);

int main(int argc, char *argv[])
{
    Entry* apple;
    apple = malloc(sizeof(Entry));
    destroyEntry(*(apple));
    return 0;
}

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Wenn ich es durchführe valgrind mit --leak-check=full --track-origins=yeserhalte ich folgenden Fehler:

==20674== Invalid free() / delete / delete[] / realloc()
==20674==    at 0x4028E58: free (vg_replace_malloc.c:427)
==20674==    by 0x80485B2: destroyEntry (testing.c:53)
==20674==    by 0x8048477: main (testing.c:26)
==20674==  Address 0xbecc0070 is on thread 1's stack

Ich denke, dieser Fehler bedeutet, dass die destroyEntry Die Funktion darf den ausdrücklich in main zugewiesenen Speicher nicht ändern. Ist das richtig? Warum kann ich nicht einfach free der Speicher, den ich zugewiesen habe main in einer anderen Funktion? (Und ist dieses Verhalten irgendwie spezifisch für main?)

  • +1 für klare Frage und SSCCE.

    – Matteo Italien

    17. Juni 2012 um 12:19 Uhr

  • @MatteoItalia, von dem ich noch nie gehört hatte SSCCE Vor. Auf jeden Fall ein gutes Konzept. Danke, dass du mich darauf aufmerksam gemacht hast.

    – Jeff Tratner

    17. Juni 2012 um 12:27 Uhr

Immer wenn Sie einen Parameter an eine Funktion übergeben, wird eine Kopie erstellt, und die Funktion arbeitet mit dieser Kopie. In Ihrem Fall versuchen Sie es also free eine Kopie des ursprünglichen Objekts, was keinen Sinn ergibt.

Sie sollten Ihre Funktion ändern, um einen Zeiger zu akzeptieren, und dann können Sie ihn aufrufen free direkt auf diesem Zeiger.

  • Also nur um klar zu sein, vorbei *(apple) in die DestroyEntry Funktion erstellt eine neue Entry struct, die vom Original getrennt ist apple eines?

    – Benutzer124384

    8. Februar 2019 um 20:41 Uhr

Benutzeravatar von LihO
Liho

Dies wird als Wert übergeben, was bedeutet, dass eine Kopie erstellt wird, sodass Sie versuchen, den Speicher freizugeben, in dem sich die lokale Variable befindet entry wohnt. Beachten Sie, dass entry ist ein Objekt mit automatischer Speicherdauer und Speicher, in dem es sich befindet, wird automatisch freigegeben, wenn Ihr Programm den Bereich verlässt destroyEntry Funktion.

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Ihre Funktion sollte einen Zeiger annehmen (Übergabe als Referenz):

void destroyEntry(Entry *entry)
{
    free(entry);
}

Dann statt destroyEntry(*(apple)); du rufst einfach an destroyEntry(apple);. Beachten Sie, dass, wenn keine andere Funktionalität damit verbunden ist destroyEntry Funktion, es ist überflüssig und es ist besser, einfach direkt anzurufen free(apple).

Benutzeravatar von gkimsey
gkimsey

Die anderen Antworten hier weisen auf das Hauptproblem hin – da Sie Ihren Apfel dereferenzieren, wenn Sie destrueEntry in main () aufrufen, wird er als Referenz übergeben und eine Kopie erstellt.

Selbst wenn Sie Ihr Problem kennen, hilft es, zum Fehler zurückzugehen und zu versuchen, den Text dessen, was Sie sehen, mit dem Problem zu verbinden, damit Sie es beim nächsten Auftreten wahrscheinlicher schnell herausfinden. Ich finde, dass C- und C++-Fehler manchmal wahnsinnig mehrdeutig erscheinen können.

Wenn ich Probleme beim Freigeben von Zeigern oder beim Löschen von Objekten habe, drucke ich im Allgemeinen gerne Adressen aus, besonders direkt, wenn ich sie zuweise und wenn ich versuche, sie freizugeben. valgrind hat Ihnen bereits die Adresse des schlechten Zeigers gegeben, aber es hilft, ihn mit einem guten zu vergleichen.

int main()
{
  Entry * apple;
  apple = malloc(sizeof(Entry));
  printf("apple's address = %p", apple);  // Prints the address of 'apple'
  free(apple);   // You know this will work
}

Danach würden Sie feststellen, dass die printf () -Anweisung Ihnen eine Adresse wie 0x8024712 (nur eine Adresse im richtigen allgemeinen Bereich) gegeben hat, aber Ihre valgrind-Ausgabe gab 0x4028E58. Sie würden feststellen, dass sie sich an zwei sehr unterschiedlichen Orten befinden (tatsächlich befindet sich “0x4 …” auf dem Stapel, nicht auf dem Haufen, von dem malloc () zuweist, aber ich gehe davon aus, wenn Sie gerade erst anfangen noch keine rote Flagge für Sie), damit Sie wissen, dass Sie versuchen, Speicher an der falschen Stelle freizugeben, daher “invalid free()”.

Von dort aus können Sie sich sagen: “Okay, irgendwie wird mein Zeiger beschädigt.” Sie haben Ihr Problem bereits auf ein kleines, kompilierbares Beispiel reduziert, sodass Sie nicht lange brauchen werden, um es von dort aus zu lösen.

TL;DR – Wenn Sie auf zeigerbezogene Fehler stoßen, versuchen Sie, die Adressen auszudrucken oder sie in Ihrem bevorzugten Debugger zu finden. Oft weist es dich zumindest in die richtige Richtung.

Nichts davon soll Sie davon abhalten, Ihre Frage natürlich auf Stack Exchange zu posten. Hunderte von Programmierern werden wahrscheinlich davon profitieren, wenn Sie dies getan haben.

  • Vielen Dank, dass Sie dies gepostet haben … es ist ein wirklich netter Punkt, um Fehler mit Zeigern und dergleichen herauszufinden. Ich habe etwas über Stack vs. Heap gelernt, wusste aber nicht, dass sie verallgemeinerte Speicheradressen hatten. (Also, NULL –> 0x0, stack–>0x4 und Haufen ist etwas anderes?).

    – Jeff Tratner

    20. Juni 2012 um 17:05 Uhr

  • Außerdem kann ich Ihren Beitrag nicht um ein Zeichen ändern, aber in der printf-Zeichenfolge: "apple's address = %d" die Formatzeichenfolge sollte wirklich sein %pnicht %dRechts?

    – Jeff Tratner

    20. Juni 2012 um 17:06 Uhr

  • Ich kann Ihnen nicht mit 100%iger Sicherheit sagen, dass Heap-Adressen immer ‘0x8 …’ und Stack-Adressen immer ‘0x4 …’ sein werden. Um ehrlich zu sein, mache ich heutzutage hauptsächlich Embedded-Programmierung, wo der Adressraum sehr gut definiert ist und Ihr Prozess der einzige ist, der läuft. Mein Verständnis ist jedoch, dass auf x86 jeder Prozess seinen eigenen virtuellen Adressraum erhält, und meiner Erfahrung nach scheinen diese “Regeln” zu gelten. Danke für den Hinweis auf das %d-Problem. Es funktioniert (insofern es die Adresse druckt), aber es ist schwerer zu lesen. Normalerweise verwende ich selbst “0x%x”, da ich es gewohnt bin, es zu sehen.

    – gkimsey

    25. Juni 2012 um 14:36 ​​Uhr

1415500cookie-checkFreier Speicher, der einer anderen Funktion zugewiesen wurde?

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

Privacy policy