Kiedy uruchamiam mój program (C ++), zawiesza się i wyświetla ten błąd.
* wykryto glibc * ./load: podwójne zwolnienie lub uszkodzenie (! prev): 0x0000000000c6ed50 ***
Jak mogę wyśledzić błąd?
Próbowałem użyć std::cout
instrukcji print ( ), ale bez powodzenia. Czy mogłoby gdb
to ułatwić?
NULL
wskaźniki (co maskuje błędy, które w przeciwnym razie zostałyby wyłapane, jak ładnie pokazuje to pytanie), ale nikt nie sugeruje, aby w ogóle nie robić ręcznego zarządzania pamięcią, co jest bardzo możliwe w C ++. Nie pisałemdelete
od lat. (I tak, mój kod jest krytyczny dla wydajności. W przeciwnym razie nie zostałby napisany w C ++.)NULL
wprowadzenie wskaźników może spowodować wcześniejszą awarię programu.NULL
wskazywania wskaźników jest zapobieganie wybuchowi sekundydelete ptr;
- co maskuje błąd, ponieważ ta sekundadelete
nigdy nie powinna była mieć miejsca. (Służy również do sprawdzania, czy wskaźnik nadal wskazuje prawidłowy obiekt. Ale to rodzi pytanie, dlaczego masz wskaźnik w zakresie, który nie ma obiektu, na który można by wskazać.)Odpowiedzi:
Jeśli używasz glibc, możesz ustawić
MALLOC_CHECK_
zmienną środowiskową na2
, spowoduje to, że glibc użyje wersji tolerującej błędymalloc
, co spowoduje przerwanie programu w miejscu, w którym podwójne zwolnienie zostanie wykonane.Możesz to ustawić z gdb, używając
set environment MALLOC_CHECK_ 2
polecenia przed uruchomieniem programu; program powinien przerwać działanie, zfree()
wywołaniem widocznym w śladzie wstecznym.zobacz stronę
malloc()
podręcznika, aby uzyskać więcej informacjiźródło
MALLOC_CHECK_2
faktycznie naprawiło mój podwójny problem z wolnością (chociaż nie jest naprawiany, jeśli jest tylko w trybie debugowania)Istnieją co najmniej dwie możliwe sytuacje:
W przypadku pierwszego zdecydowanie sugeruję ZEROWANIE wszystkich usuniętych wskaźników.
Masz trzy możliwości:
źródło
Możesz użyć gdb, ale najpierw spróbuję Valgrind . Zobacz skróconą instrukcję obsługi .
Krótko mówiąc, Valgrind instrumentuje twój program, aby mógł wykryć kilka rodzajów błędów w używaniu dynamicznie przydzielonej pamięci, takich jak podwójne zwolnienia i zapisy poza koniec przydzielonych bloków pamięci (co może uszkodzić stertę). Wykrywa i zgłasza błędy, gdy tylko się pojawią , wskazując w ten sposób bezpośrednio przyczynę problemu.
źródło
Trzy podstawowe zasady:
NULL
po zwolnieniuNULL
przed zwolnieniem.NULL
na początku.Połączenie tych trzech działa całkiem nieźle.
źródło
Możesz użyć
valgrind
do debugowania.#include<stdio.h> #include<stdlib.h> int main() { char *x = malloc(100); free(x); free(x); return 0; } [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ ./t1 *** glibc detected *** ./t1: double free or corruption (top): 0x00000000058f7010 *** ======= Backtrace: ========= /lib64/libc.so.6[0x3a3127245f] /lib64/libc.so.6(cfree+0x4b)[0x3a312728bb] ./t1[0x400500] /lib64/libc.so.6(__libc_start_main+0xf4)[0x3a3121d994] ./t1[0x400429] ======= Memory map: ======== 00400000-00401000 r-xp 00000000 68:02 30246184 /home/sand/testbox/t1 00600000-00601000 rw-p 00000000 68:02 30246184 /home/sand/testbox/t1 058f7000-05918000 rw-p 058f7000 00:00 0 [heap] 3a30e00000-3a30e1c000 r-xp 00000000 68:03 5308733 /lib64/ld-2.5.so 3a3101b000-3a3101c000 r--p 0001b000 68:03 5308733 /lib64/ld-2.5.so 3a3101c000-3a3101d000 rw-p 0001c000 68:03 5308733 /lib64/ld-2.5.so 3a31200000-3a3134e000 r-xp 00000000 68:03 5310248 /lib64/libc-2.5.so 3a3134e000-3a3154e000 ---p 0014e000 68:03 5310248 /lib64/libc-2.5.so 3a3154e000-3a31552000 r--p 0014e000 68:03 5310248 /lib64/libc-2.5.so 3a31552000-3a31553000 rw-p 00152000 68:03 5310248 /lib64/libc-2.5.so 3a31553000-3a31558000 rw-p 3a31553000 00:00 0 3a41c00000-3a41c0d000 r-xp 00000000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 3a41c0d000-3a41e0d000 ---p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 3a41e0d000-3a41e0e000 rw-p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 2b1912300000-2b1912302000 rw-p 2b1912300000 00:00 0 2b191231c000-2b191231d000 rw-p 2b191231c000 00:00 0 7ffffe214000-7ffffe229000 rw-p 7ffffffe9000 00:00 0 [stack] 7ffffe2b0000-7ffffe2b4000 r-xp 7ffffe2b0000 00:00 0 [vdso] ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vsyscall] Aborted [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck ./t1 ==20859== Memcheck, a memory error detector ==20859== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20859== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20859== Command: ./t1 ==20859== ==20859== Invalid free() / delete / delete[] ==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20859== by 0x4004FF: main (t1.c:8) ==20859== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd ==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20859== by 0x4004F6: main (t1.c:7) ==20859== ==20859== ==20859== HEAP SUMMARY: ==20859== in use at exit: 0 bytes in 0 blocks ==20859== total heap usage: 1 allocs, 2 frees, 100 bytes allocated ==20859== ==20859== All heap blocks were freed -- no leaks are possible ==20859== ==20859== For counts of detected and suppressed errors, rerun with: -v ==20859== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 ==20899== Memcheck, a memory error detector ==20899== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20899== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20899== Command: ./t1 ==20899== ==20899== Invalid free() / delete / delete[] ==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20899== by 0x4004FF: main (t1.c:8) ==20899== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd ==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20899== by 0x4004F6: main (t1.c:7) ==20899== ==20899== ==20899== HEAP SUMMARY: ==20899== in use at exit: 0 bytes in 0 blocks ==20899== total heap usage: 1 allocs, 2 frees, 100 bytes allocated ==20899== ==20899== All heap blocks were freed -- no leaks are possible ==20899== ==20899== For counts of detected and suppressed errors, rerun with: -v ==20899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$
Jedna możliwa poprawka:
#include<stdio.h> #include<stdlib.h> int main() { char *x = malloc(100); free(x); x=NULL; free(x); return 0; } [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ ./t1 [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 ==20958== Memcheck, a memory error detector ==20958== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20958== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20958== Command: ./t1 ==20958== ==20958== ==20958== HEAP SUMMARY: ==20958== in use at exit: 0 bytes in 0 blocks ==20958== total heap usage: 1 allocs, 1 frees, 100 bytes allocated ==20958== ==20958== All heap blocks were freed -- no leaks are possible ==20958== ==20958== For counts of detected and suppressed errors, rerun with: -v ==20958== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$
Zajrzyj na blog dotyczący korzystania z Valgrind Link
źródło
Dzięki nowoczesnym kompilatorom C ++ możesz używać środków dezynfekujących do śledzenia.
Przykładowy przykład:
Mój program:
$cat d_free.cxx #include<iostream> using namespace std; int main() { int * i = new int(); delete i; //i = NULL; delete i; }
Skompiluj ze środkami dezynfekującymi adres:
# g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g
Wykonać :
# ./a.out ================================================================= ==4836==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0: #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 #1 0x400b2c in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:11 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) #3 0x400a08 (/media/sf_shared/jkr/cpp/d_free/a.out+0x400a08) 0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014) freed by thread T0 here: #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 #1 0x400b1b in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:9 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) previously allocated by thread T0 here: #0 0x7f35b2d7a040 in operator new(unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:80 #1 0x400ac9 in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:8 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) SUMMARY: AddressSanitizer: double-free /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 in operator delete(void*, unsigned long) ==4836==ABORTING
Aby dowiedzieć się więcej na temat środków odkażających można sprawdzić to czy ten lub wszelkie nowoczesne kompilatory C ++ (np gcc, dzyń itd.) Dokumentacje.
źródło
Czy używasz inteligentnych wskaźników, takich jak Boost
shared_ptr
? Jeśli tak, sprawdź, czy w dowolnym miejscu bezpośrednio używasz surowego wskaźnika, wywołującget()
. Uważam, że jest to dość powszechny problem.Na przykład wyobraź sobie scenariusz, w którym surowy wskaźnik jest przekazywany (na przykład jako funkcja obsługi wywołania zwrotnego) do twojego kodu. Możesz zdecydować się przypisać to do inteligentnego wskaźnika, aby poradzić sobie z liczeniem referencji itp. Duży błąd: Twój kod nie jest właścicielem tego wskaźnika, chyba że wykonasz głęboką kopię. Kiedy twój kod zostanie wykonany za pomocą inteligentnego wskaźnika, zniszczy go i spróbuje zniszczyć pamięć, na którą wskazuje, ponieważ uważa, że nikt inny go nie potrzebuje, ale kod wywołujący spróbuje go usunąć, a otrzymasz podwójną wolny problem.
Oczywiście może to nie być twój problem. Najprościej jest to przykład, który pokazuje, jak to się może stać. Pierwsze usunięcie jest w porządku, ale kompilator wyczuwa, że usunął już tę pamięć i powoduje problem. Dlatego przypisanie 0 do wskaźnika bezpośrednio po usunięciu jest dobrym pomysłem.
int main(int argc, char* argv[]) { char* ptr = new char[20]; delete[] ptr; ptr = 0; // Comment me out and watch me crash and burn. delete[] ptr; }
Edycja: zmieniono
delete
nadelete[]
, ponieważ ptr jest tablicą znaków.źródło
Wiem, że to bardzo stary wątek, ale jest to najpopularniejsza wyszukiwarka Google dla tego błędu i żadna z odpowiedzi nie wspomina o częstej przyczynie błędu.
Który zamyka plik, który już zamknąłeś.
Jeśli nie zwracasz uwagi i masz dwie różne funkcje zamykające ten sam plik, druga z nich wygeneruje ten błąd.
źródło