« Dlaczego nie działa mój program, pytanie/odpowiedź »
Niniejszy dokument zawiera wskazówki, które ułatwiają samodzielne rozwiązywanie problemów typu: "Napisany program nie działa wcale lub nie działa poprawnie". (pytanie/odpowiedź)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: pekfos
FAQ

Dlaczego nie działa mój program

[pytanie/odpowiedź] Niniejszy dokument zawiera wskazówki, które ułatwiają samodzielne rozwiązywanie problemów typu: "Napisany program nie działa wcale lub nie działa poprawnie".

Wprowadzenie

Gdyby odpowiedź na pytanie "dlaczego nie działa mój program" była prosta, to nie byłoby na tym forum, w chwili pisania tego tekstu, 21507 tematów. Najlepszym sposobem na błędy jest mieszanka wiedzy, doświadczenia i uporu, w bliżej nieokreślonych proporcjach. Jeśli zadajesz te pytanie, to można się domyślać, że raczej problem w braku wiedzy i doświadczenia, ale jeśli masz trochę uporu, to poniżej znajduje się lista często popełnianych błędów, którą możesz sprawdzić, zanim zadasz pytanie na forum. A gdy już zadasz, to niech chociaż temat nie nazywa się 'Dlaczego mi nie działa program?', bo się pogniewamy..

Jesteś pewien, że powinno działać?

Jeśli eksperymentujesz z kursem, czy książką i piszesz dużo małych programów, upewnij się, że uruchamiasz program, który myślisz, że uruchamiasz.
Jeśli masz założony projekt z wieloma plikami źródłowymi i coś działa podejrzanie, spróbuj zrobić rebuild. Więcej o tym przypadku w » Kurs C++ / FAQNapisany program działa inaczej niż powinien pytanie/odpowiedź

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. W 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:
AdresOpis
0xCCCCCCCCPamięć niezainicjalizowana na stosie
0xCDCDCDCDPamięć niezainicjalizowana na stercie (dynamicznie zaalokowana)
0xDDDDDDDDPamięć zwolniona free()
0xFEEEFEEEPamięć zwolniona HeapFree()
0xFDFDFDFDNo man's land, dodatkowe bajty przed i po zaalokowanej pamięci
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:
HexDecOpis
0xC0000005-1073741819Błąd segmentacji (błędne odwołanie do pamięci - pusty wskaźnik, lub błędny wskaźnik)
0xC00000FD-1073741571Stack overflow - nieskończona rekurencja, lub skończona, ale zbyt głęboka rekurencja.
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.

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.

Co zrobić gdy nic z powyższego nie pomogło

Samodzielne rozwiązywanie problemów jest bardzo ważną umiejętnością, a debugger jest bardzo pomocnym narzędziem do tego celu. Spróbuj prześledzić działanie programu debuggerem od ogółu, do szczegółu - wchodząc w poszczególne funkcje jeśli zauważysz, że zawartość zmiennych po ich wykonaniu jest błędna. Jeśli błąd pojawia się po dłuższym działaniu programu, wypisuj komunikaty diagnostyczne w konsoli, albo z poziomu debuggera. Określenie okoliczności błędu i ich odtwarzanie znacząco ułatwia i przyspiesza odnajdywanie błędów.