Program się wysypuje?
Jeśli po uruchomieniu programu, lub wykonaniu w nim określonej akcji, program przestaje działać, to najlepszą rzeczą jaką możesz w tym momencie zrobić, to uruchomić program pod debuggerem i spróbować powtórzyć okoliczności, w których wystąpił błąd.
Debugowanie w Visual Studio 2017W tym momencie program zostanie zatrzymany i twoje środowisko pokaże Ci linię która
bezpośrednio wywołała błąd. Są szanse, że trafisz do obcego kodu, lub kod nie będzie w ogóle dostępny. W tej sytuacji, przejdź w dół call stacka do kodu, który będzie Ci mówił nieco więcej. Być może u podstawy błędu będzie dereferencja pustego wskaźnika, albo przekroczenie zakresu tablicy. Crash może być wywołany przez, na przykład, dzielenie przez zero, ale najczęściej jest to efekt błędnego odwołania do pamięci.
Adres użyty w błędnym odwołaniu również może informować o naturze problemu. W celu łatwiejszego identyfikowania błędnego użycia pamięci, pamięć niezainicjalizowana, czy zwolniona bywa nadpisywana magicznymi liczbami. Poniżej wartości które można spotkać pod Windowsem:
Jeśli uruchamiasz swój program przez środowisko programistyczne, to po jego zakończeniu zwykle gdzieś jest wypisywany kod zakończenia programu. W przypadku crasha, kod ten będzie jakąś bardzo dużą liczbą (ujemną lub dodatnią). Kod może być pokazany jako np. -1073741819, ale powinien być też tam w postaci szesnastkowej, a więc C0000005, lub 0xC0000005. Jeśli liczba jest pokazana tylko w systemie dziesiętnym, to konwersji można dokonać kalkulatorem Windows w widoku "Programisty" (ustaw długość liczby na DWORD). Ten kod jest cenną informacją dostarczaną przez system operacyjny:
Te kody są najczęściej spotykane. Jeśli masz coś innego, wpisz kod w Google, najlepiej w postaci szesnastkowej. Opisy można znaleźć też tu:
https://msdn.microsoft.com/en-us/library/cc704588.aspx.
Gorsze błędy
Wyżej zaznaczyłem, że debugger pokazuje bezpośrednią przyczynę błędu aplikacji, a więc instrukcję, która wywołała wyjątek, a nie błąd w kodzie. Odwołanie pod adres X wywołuje problem, ale tak na prawdę problem jest w tym, że ten adres jest błędny, wcześniejszy kod niedostatecznie zadbał o poprawność tego adresu, lub go zepsuł. Przez te rozróżnienie, faktyczny bug w aplikacji może być daleko od miejsca, w którym bug się ujawnia. Nawet: bardzo daleko. To prowadzi do odrębnej klasy problemów z sypaniem się programu, którym dawno temu nadałem nieoficjalną nazwę
najgorszych problemów jakich można nabawić się w C/C++. Zwykle scenariusz wygląda tak, że gdzieś w programie jest błędne odwołanie do pamięci, ale nie dość błędne, żeby wywołać wyjątek - a więc trafiona pamięć należy do tego samego programu. Później przychodzi użyć tej uszkodzonej pamięci i dochodzi do błędu. Cechą charakterystyczną takich błędów jest to, że nie stoi za nimi żadna większa logika i nie można przez to odtworzyć błędu. Bywa, że błąd pojawia się raz na kilka-kilkadziesiąt uruchomień programu, nigdy pod debuggerem i tylko przy włączonych optymalizacjach. Jeśli masz problem spełniający te objawy, ale ujawniający się niezależnie od optymalizacji kompilatora, zwiększ poziom ostrzeżeń, szukaj użyć niezainicjalizowanych zmiennych, zwykle wskaźników lub indeksów. Jeśli problem ujawnia się tylko przy włączonych optymalizacjach, do możliwych przyczyn dochodzą różne niezdefiniowane zachowania.