
Saya menyambut Anda, para Khabrovchia!
Hari ini saya ingin membuka sedikit cahaya tentang cara menangani kebocoran memori di C atau C ++.
Sudah ada dua artikel tentang Habré, yaitu: Kami menangani kebocoran memori (C ++ CRT) dan Kebocoran memori di C ++: Visual Leak Detector . Namun, saya percaya bahwa mereka tidak diungkapkan secara memadai, atau metode ini mungkin tidak memberikan hasil yang Anda butuhkan, jadi saya ingin melihat sejauh mungkin semua metode yang tersedia untuk membuat hidup Anda lebih mudah.
Windows - pengembangan
Mari kita mulai dengan Windows, yaitu pengembangan untuk Visual Studio, karena kebanyakan programmer pemula menulis khusus untuk IDE ini.

Untuk memahami apa yang terjadi, saya menerapkan contoh nyata:
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:
Dan juga ada Student.h
dan Student.c
di mana struktur dan fungsi dinyatakan.
Ada tugas: untuk menunjukkan tidak adanya kebocoran memori. Hal pertama yang terlintas dalam pikiran adalah CRT. Semuanya cukup sederhana di sini.
Di bagian atas file tempat main berada, tambahkan potongan kode ini:
#define __CRTDBG_MAP_ALLOC #include <crtdbg.h> #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW
Dan sebelum return 0
Anda harus mendaftarkan ini: _CrtDumpMemoryLeaks();
.
Akibatnya, dalam mode Debug, studio akan menampilkan ini:
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.
Hebat! Sekarang Anda tahu bahwa Anda memiliki kebocoran memori. Sekarang Anda perlu menghilangkan ini, jadi Anda hanya perlu mencari tahu di mana kami lupa untuk menghapus memori. Dan di sini muncul masalah: di mana, pada kenyataannya, memori ini dialokasikan?

Setelah saya ulangi semua langkah, saya menemukan bahwa memori hilang di suatu tempat di sini:
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); }
Tapi bagaimana itu? Apakah saya membebaskan semuanya? Atau tidak?
Dan di sini saya benar-benar merindukan Valgrind, dengan jejak panggilannya ...
Hasilnya, setelah 15 menit berjalan, saya menemukan analog Valgrind - Visual Leak Detector . Ini adalah perpustakaan pihak ketiga, pembungkus CRT yang berjanji untuk menunjukkan penelusuran! Ini yang saya butuhkan.
Untuk menginstalnya, Anda harus pergi ke repositori dan menemukan vld-2.5.1-setup.exe
di aset
Benar, pembaruan terakhir adalah dari waktu Visual Studio 2015, tetapi bekerja dengan Visual Studio 2019. Instalasi standar, cukup ikuti instruksi.
Untuk mengaktifkan VLD, Anda harus mendaftar #include <vld.h>
.
Keuntungan dari utilitas ini adalah Anda tidak dapat menjalankan mode debug (F5), karena semuanya ditampilkan di konsol. Pada awalnya, ini akan ditampilkan:
Visual Leak Detector read settings from: C:\Program Files (x86)\Visual Leak Detector\vld.ini Visual Leak Detector Version 2.5.1 installed.
Dan inilah yang akan dihasilkan saat memori bocor:
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.
Di sini, saya melihat jejaknya! Jadi, di mana baris kode? Di mana nama fungsinya?

Oke, janji itu ditepati, tetapi ini bukan hasil yang saya inginkan.
Masih ada satu opsi yang saya temukan di Google: snapshot memori. Ini dilakukan secara sederhana: dalam mode debug, ketika Anda bisa kembali 0, Anda harus pergi ke tab "Penggunaan Memori" di alat diagnostik dan klik "Ambil Foto". Mungkin fitur ini akan dinonaktifkan untuk Anda, seperti pada tangkapan layar pertama. Maka Anda perlu menghidupkan dan me-restart debug.
Setelah Anda mengambil gambar, Anda akan melihat ukuran di bawah tumpukan. Saya pikir ini adalah berapa banyak memori yang dialokasikan selama program. Klik pada ukuran ini. Kami akan memiliki jendela yang akan berisi objek yang disimpan di tumpukan ini. Untuk melihat informasi terperinci, Anda perlu memilih objek dan klik tombol "Instance Representation Object Instances".
Ya! Ini kemenangan! Jejak penuh dengan lokasi panggilan! Inilah yang awalnya dibutuhkan.
Linux - pengembangan
Sekarang, mari kita lihat apa yang terjadi di Linux.

Di Linux, ada utilitas valgrind. Untuk menginstal valgrind, Anda perlu mendaftarkan sudo apt install valgrind
di konsol (Untuk keluarga Debian).
Saya menulis sebuah program kecil yang mengisi array dinamis, tetapi pada saat yang sama, memori tidak dihapus:
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; }
Setelah mengkompilasi program menggunakan CLang, kami mendapatkan file .out, yang kami lemparkan ke valgrind.
Menggunakan perintah valgrind ./a.out
. Bagaimana valgrind bekerja, saya pikir masuk akal untuk menggambarkannya di artikel terpisah, dan sekarang, bagaimana program berjalan, valgrind akan menampilkan ini:
==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)
Dengan demikian, valgrind sejauh ini menunjukkan berapa banyak memori yang hilang. Untuk melihat di mana memori dialokasikan, Anda perlu menulis --leak-check=full
, dan kemudian, valgrind, selain yang di atas, akan menampilkan ini:
==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)
Tentu saja, tidak ada string yang ditentukan di sini, tetapi suatu fungsi telah ditentukan, yang merupakan kabar baik.
Ada alternatif untuk valgrind, seperti strace atau Dr.Memory, tetapi saya tidak menggunakannya, dan mereka terutama digunakan di mana valgrind tidak berdaya.
Kesimpulan
Saya senang bahwa saya dihadapkan dengan masalah menemukan kebocoran memori di Visual Studio, karena saya belajar banyak alat baru, kapan dan bagaimana cara menggunakannya, dan mulai memahami cara kerja alat ini.
Terima kasih atas perhatian Anda, kode penulisan yang baik untuk Anda!