Błędy oprogramowania są powszechne. Łatwo je zrobić, trudno je znaleźć. W tym rozdziale omówimy tematy związane ze znajdowaniem i usuwaniem błędów w naszych programach C++, w tym nauczymy się korzystać ze zintegrowanego debugera, który jest częścią naszego IDE.
Chociaż narzędzia i techniki debugowania nie są częścią standardu C++, nauczenie się znajdowania i usuwania błędów w pisanych programach jest niezwykle ważną częścią odniesienia sukcesu jako programista. Dlatego poświęcimy trochę czasu na omówienie takich tematów, tak aby w miarę jak pisane przez Ciebie programy staną się bardziej złożone, Twoja zdolność do diagnozowania i rozwiązywania problemów będzie rosła w podobnym tempie.
Jeśli masz doświadczenie w debugowaniu programów w innym języku programowania, większość z tych zagadnień będzie ci znana.
Błędy składniowe
Programowanie może być wyzwaniem, a C++ jest dość dziwacznym językiem. Jeśli połączysz te dwa elementy, otrzymasz wiele sposobów na popełnienie błędów. Błędy ogólnie dzielą się na jedną z dwóch kategorii: błędy składniowe i błędy semantyczne (błędy logiczne).
A składnią błąd występują, gdy piszesz instrukcję, która nie jest poprawna zgodnie z gramatyką języka C++. Obejmuje to błędy, takie jak brakujące średniki, niedopasowane nawiasy lub nawiasy klamrowe itp. Na przykład następujący program zawiera sporo błędów składniowych:
#include <iostream>
int main( // missing closing brace
{
int 1x; // variable name can't start with number
std::cout << "Hi there"; << x +++ << '\n'; // extraneous semicolon, operator+++ does not exist
return 0 // missing semicolon at end of statement
}Na szczęście kompilator wykryje błędy składniowe i wyświetli ostrzeżenie lub błąd kompilacji, dzięki czemu można łatwo zidentyfikować i naprawić problem. Potem pozostaje już tylko ponowna kompilacja, aż pozbędziesz się wszystkich błędów.
Błędy semantyczne
A Błąd semantyczny to błąd w znaczeniu. Występują one, gdy instrukcja jest poprawna składniowo, ale albo narusza inne zasady języka, albo nie robi tego, co zamierzył programista.
Kompilator może wychwycić pewnego rodzaju błędy semantyczne. Typowe przykłady obejmują użycie niezadeklarowanej zmiennej, niezgodność typów (kiedy używamy gdzieś obiektu o niewłaściwym typie) itp…
Na przykład następujący program zawiera kilka błędów semantycznych w czasie kompilacji:
int main()
{
5 = x; // x not declared, cannot assign a value to 5
return "hello"; // "hello" cannot be converted to an int
}Inne błędy semantyczne pojawiają się dopiero w czasie wykonywania. Czasami spowodują one awarię programu, na przykład w przypadku dzielenia przez zero:
#include <iostream>
int main()
{
int a { 10 };
int b { 0 };
std::cout << a << " / " << b << " = " << a / b << '\n'; // division by 0 is undefined in mathematics
return 0;
}Często powodują one po prostu złą wartość lub zachowanie:
#include <iostream>
int main()
{
int x; // no initializer provided
std::cout << x << '\n'; // Use of uninitialized variable leads to undefined result
return 0;
}lub
#include <iostream>
int add(int x, int y) // this function is supposed to perform addition
{
return x - y; // but it doesn't due to the wrong operator being used
}
int main()
{
std::cout << "5 + 3 = " << add(5, 3) << '\n'; // should produce 8, but produces 2
return 0;
}lub
#include <iostream>
int main()
{
return 0; // function returns here
std::cout << "Hello, world!\n"; // so this never executes
}W powyższym przykładzie błędy są dość łatwe do wykrycia. Jednak w większości nietrywialnych programów błędy semantyczne w czasie wykonywania nie są łatwe do znalezienia, patrząc na kod. Tutaj mogą się przydać techniki debugowania.

