Piszę wieloplatformowy program w C ++ dla systemów Windows i Unix. Po stronie okna kod będzie się kompilował i wykonywał bez problemu. Po stronie systemu Unix będzie się kompilować, jednak gdy próbuję go uruchomić, pojawia się błąd segmentacji. Moje początkowe przeczucie jest takie, że jest problem ze wskazówkami.
Jakie są dobre metodologie wyszukiwania i naprawiania błędów błędów segmentacji?
źródło
g
w kontekścieCMake
?cmake -DCMAKE_BUILD_TYPE=Debug
.Czasami sama awaria nie jest prawdziwą przyczyną problemu - być może pamięć została zniszczona na wcześniejszym etapie, ale uszkodzenie ujawniło się dopiero po pewnym czasie. Sprawdź valgrind , który ma wiele sprawdzeń pod kątem problemów ze wskaźnikami (w tym sprawdzanie granic tablicy). Poinformuje Cię, gdzie zaczyna się problem , a nie tylko w linii, w której występuje awaria.
źródło
Zanim pojawi się problem, staraj się go jak najbardziej unikać:
Użyj odpowiednich narzędzi do debugowania. W systemie Unix:
Z GCC możesz również użyć błotnikaZ GCC, Clang i od października eksperymentalnie MSVC możesz użyć Address / Memory Sanitizer . Potrafi wykryć pewne błędy, których Valgrind nie robi, a utrata wydajności jest mniejsza. Jest używany przez kompilację z-fsanitize=address
flagą.Na koniec poleciłbym zwykłe rzeczy. Im bardziej Twój program jest czytelny, łatwy w utrzymaniu, przejrzysty i schludny, tym łatwiej będzie go debugować.
źródło
W systemie Unix możesz użyć
valgrind
do znalezienia problemów. Jest darmowy i potężny. Jeśli wolisz zrobić to sam, możesz przeciążyć operatorynew
idelete
, aby ustawić konfigurację, w której masz 1 bajt0xDEADBEEF
przed i po każdym nowym obiekcie. Następnie śledź, co się dzieje w każdej iteracji. Może to nie przechwycić wszystkiego (nie masz gwarancji, że nawet dotkniesz tych bajtów), ale działało to dla mnie w przeszłości na platformie Windows.źródło
new
idelete
może być bardzo przydatne, użycie-fsanitize=address
jest lepszą opcją, ponieważ kompilator będzie kompilował się w wykrywaniu problemów w czasie wykonywania i automatycznie zrzuci pamięć na ekran, co znacznie ułatwia debugowanie.new
idelete
, możesz zawijać,malloc
jeśli używaszgcc
. Zobacz--wrap=symbol
. Zamierzam to zrobić w kodzie wydania, aby uzyskać kilka diagnostyki w czasie wykonywania.Tak, jest problem ze wskazówkami. Bardzo prawdopodobne, że używasz takiego, który nie został poprawnie zainicjowany, ale możliwe jest również, że psujesz zarządzanie pamięcią przez podwójne zwolnienia lub coś takiego.
Aby uniknąć niezainicjowanych wskaźników jako zmiennych lokalnych, spróbuj zadeklarować je tak późno, jak to możliwe, najlepiej (a to nie zawsze jest możliwe), kiedy można je zainicjować znaczącą wartością. Przekonaj się, że będą miały wartość, zanim zostaną użyte, badając kod. Jeśli masz z tym trudności, zainicjuj je jako stałą wskaźnika zerowego (zwykle zapisaną jako
NULL
lub0
) i sprawdź je.Aby uniknąć niezainicjowanych wskaźników jako wartości składowych, upewnij się, że są one poprawnie zainicjowane w konstruktorze i prawidłowo obsługiwane w konstruktorach kopiujących i operatorach przypisania. Nie polegaj na
init
funkcji do zarządzania pamięcią, chociaż możesz do innej inicjalizacji.Jeśli Twoja klasa nie potrzebuje konstruktorów kopiujących ani operatorów przypisania, możesz zadeklarować je jako prywatne funkcje składowe i nigdy ich nie definiować. Spowoduje to błąd kompilatora, jeśli zostaną użyte jawnie lub niejawnie.
W stosownych przypadkach używaj inteligentnych wskaźników. Dużą zaletą jest to, że jeśli będziesz się ich trzymać i konsekwentnie używać, możesz całkowicie uniknąć pisania
delete
i nic nie zostanie podwójnie usunięte.Jeśli to możliwe, używaj ciągów C ++ i klas kontenerów zamiast ciągów i tablic w stylu C. Rozważ użycie
.at(i)
zamiast[i]
, ponieważ wymusi to sprawdzanie granic. Sprawdź, czy Twój kompilator lub bibliotekę można ustawić tak, aby sprawdzał ograniczenia[i]
, przynajmniej w trybie debugowania. Błędy segmentacji mogą być spowodowane przepełnieniem bufora, które zapisuje śmieci po idealnie dobrych wskaźnikach.Robienie tych rzeczy znacznie zmniejszy prawdopodobieństwo błędów segmentacji i innych problemów z pamięcią. Bez wątpienia nie naprawią wszystkiego i dlatego powinieneś używać valgrind od czasu do czasu, kiedy nie masz problemów, a valgrind i gdb, kiedy to zrobisz.
źródło
Nie znam żadnej metodologii, aby naprawić takie rzeczy. Nie sądzę, by można było wymyślić taki, ponieważ sam problem polega na tym, że zachowanie twojego programu jest niezdefiniowane (nie znam żadnego przypadku, gdy SEGFAULT nie został spowodowany przez jakiś rodzaj UB) .
Istnieją różne „metodologie” pozwalające uniknąć problemu, zanim się on pojawi. Jednym z ważnych jest RAII.
Poza tym musisz po prostu rzucić w to swoje najlepsze psychiczne energie.
źródło