
Ich grüße Sie, Khabrovchians!
Heute möchte ich ein wenig erläutern, wie mit Speicherverlusten in C oder C ++ umgegangen werden kann.
Es gibt bereits zwei Artikel zu Habré, nämlich: Wir beschäftigen uns mit Speicherlecks (C ++ CRT) und Speicherlecks in C ++: Visual Leak Detector . Ich bin jedoch der Meinung, dass sie nicht ausreichend offengelegt werden oder dass diese Methoden möglicherweise nicht das gewünschte Ergebnis liefern. Deshalb möchte ich so weit wie möglich alle verfügbaren Methoden ausmachen, um Ihnen das Leben zu erleichtern.
Windows - Entwicklung
Beginnen wir mit Windows, der Entwicklung für Visual Studio, da die meisten unerfahrenen Programmierer speziell für diese IDE schreiben.

Um zu verstehen, was passiert, wende ich ein reales Beispiel an:
Main.cstruct Student create_student(); void ControlMenu(); int main() { ControlMenu(); return 0; } void ShowListMenu(int kX) { char listMenu[COUNT_LIST_MENU][55] = { {"Read students from file"}, {"Input student and push"}, {"Input student and push it back"}, {"Input student and push it after student"}, {"Delete last student"}, {"Write students to file"}, {"Find student"}, {"Sort students"}, {"Show list of students"}, {"Exit"} }; for (int i = 0; i < COUNT_LIST_MENU; i++) { if (i == kX) { printf("%s", listMenu[i]); printf(" <=\n"); } else printf("%s\n", listMenu[i]); } } void ControlMenu() { struct ListOfStudents* list = NULL; int kX = 0, key; int exit = FALSE; ShowListMenu(kX); do { key = _getch(); switch (key) { case 72:
Und es gibt Student.h
und Student.c
in denen Strukturen und Funktionen deklariert sind.
Es gibt eine Aufgabe: das Fehlen von Speicherlecks zu demonstrieren. Das erste, was mir einfällt, ist CRT. Hier ist alles ganz einfach.
Fügen Sie oben in der Datei, in der sich main befindet, den folgenden Code hinzu:
#define __CRTDBG_MAP_ALLOC #include <crtdbg.h> #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW
Und bevor Sie return 0
, müssen Sie dies registrieren: _CrtDumpMemoryLeaks();
.
Infolgedessen gibt das Studio im Debug-Modus Folgendes aus:
Detected memory leaks! Dumping objects -> {79} normal block at 0x00A04410, 376 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete.
Großartig! Jetzt wissen Sie, dass Sie einen Speicherverlust haben. Jetzt müssen Sie dies beseitigen, damit Sie nur herausfinden müssen, wo wir vergessen haben, den Speicher zu löschen. Und hier tritt ein Problem auf: Wo wurde dieses Gedächtnis tatsächlich zugewiesen?

Nachdem ich alle Schritte wiederholt hatte, stellte ich fest, dass hier irgendwo Speicher verloren geht:
if (kX == 0) { int sizeStudents = 0; struct Student* students = (struct Student*)malloc(1 * sizeof(struct Student)); char* path = (char*)malloc(255 * sizeof(char)); printf("Put the path to file with students: "); scanf("%s", path); int size = 0; students = read_students(path, &size); if (students == NULL) { printf("Can't open this file.\n"); } else { for (int i = 0; i < size; i++) { if (i == 0) { list = init(students[i]); } else { list = add_new_elem_to_start(list, students[i]); } } } free(students); printf("\nPress any key to continue..."); getchar(); getchar(); free(path); }
Aber wie ist das Befreie ich alles? Oder nicht?
Und hier habe ich Valgrind mit seiner Anrufverfolgung wirklich vermisst ...
Als Ergebnis fand ich nach 15 Minuten zu Fuß ein Analogon von Valgrind - Visual Leak Detector . Dies ist eine Bibliothek von Drittanbietern, ein Wrapper über CRT, der versprochen hat, Tracing zu zeigen! Das ist was ich brauche.
Um es zu installieren, müssen Sie zum Repository gehen und vld-2.5.1-setup.exe
in den Assets finden
Das letzte Update stammt aus der Zeit von Visual Studio 2015, funktioniert jedoch mit Visual Studio 2019. Die Installation ist Standard. Befolgen Sie einfach die Anweisungen.
Um VLD zu aktivieren, müssen Sie #include <vld.h>
registrieren.
Der Vorteil dieses Dienstprogramms ist, dass Sie nicht im Debug-Modus (F5) ausgeführt werden können, da alles in der Konsole angezeigt wird. Ganz am Anfang wird dies angezeigt:
Visual Leak Detector read settings from: C:\Program Files (x86)\Visual Leak Detector\vld.ini Visual Leak Detector Version 2.5.1 installed.
Und hier ist, was bei einem Speicherverlust entsteht:
WARNING: Visual Leak Detector detected memory leaks! ---------- Block 1 at 0x01405FD0: 376 bytes ---------- Leak Hash: 0x555D2B67, Count: 1, Total 376 bytes Call Stack (TID 8908): ucrtbased.dll!malloc() test.exe!0x00F41946() test.exe!0x00F42E1D() test.exe!0x00F44723() test.exe!0x00F44577() test.exe!0x00F4440D() test.exe!0x00F447A8() KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xED bytes ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xBD bytes Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ Visual Leak Detector detected 1 memory leak (412 bytes). Largest number used: 3115 bytes. Total allocations: 3563 bytes. Visual Leak Detector is now exiting.
Hier sehe ich die Spur! Also, wo sind die Codezeilen? Wo sind die Funktionsnamen?

Okay, das Versprechen wurde gehalten, aber das ist nicht das Ergebnis, das ich wollte.
Es gibt noch eine Option, die ich in Google gefunden habe: eine Momentaufnahme des Speichers. Es ist ganz einfach: Wenn Sie im Debug-Modus 0 zurückgeben, müssen Sie im Diagnosetool zur Registerkarte "Speichernutzung" gehen und auf "Snapshot erstellen" klicken. Vielleicht ist diese Funktion für Sie deaktiviert, wie im ersten Screenshot. Dann müssen Sie das Debug einschalten und neu starten.
Nachdem Sie das Bild aufgenommen haben, sehen Sie eine Größe unter dem Haufen. Ich denke, das ist, wie viel Speicher während des Programms zugewiesen wurde. Klicken Sie auf diese Größe. Wir werden ein Fenster haben, das Objekte enthält, die in diesem Heap gespeichert sind. Um detaillierte Informationen anzuzeigen, müssen Sie ein Objekt auswählen und auf die Schaltfläche "Foo Object Representation Instances" klicken.
Ja Das ist ein Sieg! Vollständige Verfolgung mit Anrufortung! Dies ist, was ursprünglich benötigt wurde.
Linux - Entwicklung
Nun wollen wir sehen, was unter Linux passiert.

Unter Linux gibt es das Dienstprogramm valgrind. Um valgrind zu installieren, müssen Sie sudo apt install valgrind
in der Konsole registrieren (Für die Debian-Familie).
Ich habe ein kleines Programm geschrieben, das ein dynamisches Array ausfüllt, aber gleichzeitig wird der Speicher nicht gelöscht:
main.c #include <stdlib.h> #include <stdio.h> #define N 10 int main() { int * mas = (int *)malloc(N * sizeof(int)); for(int i = 0; i < N; i++) { *(mas+i) = i; printf("%d\t", *(mas+i)); } printf("\n"); return 0; }
Nachdem wir das Programm mit CLang kompiliert haben, erhalten wir eine .out-Datei, die wir an valgrind übergeben.
Verwenden Sie den valgrind ./a.out
. Wie valgrind funktioniert, halte ich es für sinnvoll, es in einem separaten Artikel zu beschreiben, und nun, wie das Programm abläuft, gibt valgrind Folgendes aus:
==2342== HEAP SUMMARY: ==2342== in use at exit: 40 bytes in 1 blocks ==2342== total heap usage: 2 allocs, 1 frees, 1,064 bytes allocated ==2342== ==2342== Searching for pointers to 1 not-freed blocks ==2342== Checked 68,984 bytes ==2342== ==2342== LEAK SUMMARY: ==2342== definitely lost: 40 bytes in 1 blocks ==2342== indirectly lost: 0 bytes in 0 blocks ==2342== possibly lost: 0 bytes in 0 blocks ==2342== still reachable: 0 bytes in 0 blocks ==2342== suppressed: 0 bytes in 0 blocks ==2342== Rerun with --leak-check=full to see details of leaked memory ==2342== ==2342== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==2342== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Valgrind zeigt also, wie viel Speicher verloren gegangen ist. Um zu sehen, wo der Speicher zugewiesen wurde, müssen Sie --leak-check=full
schreiben, und dann gibt valgrind zusätzlich zu den obigen Angaben --leak-check=full
:
==2348== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2348== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==2348== by 0x40053A: main (in /home/hunterlan/Habr/a.out)
Natürlich ist hier keine Zeichenfolge angegeben, aber es wurde bereits eine Funktion angegeben, was eine gute Nachricht ist.
Es gibt Alternativen zu Valgrind, wie Strace oder Dr.Memory, aber ich habe sie nicht verwendet und sie werden hauptsächlich dort verwendet, wo Valgrind machtlos ist.
Schlussfolgerungen
Ich bin froh, dass ich mit dem Problem konfrontiert war, ein Speicherverlust in Visual Studio zu finden, als ich viele neue Tools lernte, wann und wie man sie verwendet, und begann zu verstehen, wie diese Tools funktionieren.
Vielen Dank für Ihre Aufmerksamkeit, gutes Schreiben von Code an Sie!