Prawdziwa odpowiedź brzmi: nigdy tak naprawdę nie możesz być tego pewien.
Przynajmniej w przypadku nietrywialnych przypadków nie możesz być pewien, że wszystko to otrzymałeś. Rozważ następujące kwestie z artykułu Wikipedii na temat nieosiągalnego kodu :
double x = sqrt(2);
if (x > 5)
{
doStuff();
}
Jak słusznie zauważa Wikipedia, sprytny kompilator może być w stanie złapać coś takiego. Ale rozważ modyfikację:
int y;
cin >> y;
double x = sqrt((double)y);
if (x != 0 && x < 1)
{
doStuff();
}
Czy kompilator to złapie? Może. Ale aby to zrobić, będzie musiał zrobić coś więcej niż działać sqrt
przy stałej wartości skalarnej. Będzie musiał dowiedzieć się, że (double)y
zawsze będzie liczbą całkowitą (łatwą), a następnie zrozumieć matematyczny zakres sqrt
zbioru liczb całkowitych (trudny). Bardzo wyrafinowany kompilator może to zrobić dla sqrt
funkcji lub dla każdej funkcji w mat.h lub dla dowolnej funkcji o ustalonym wejściu, której dziedziny może się dowiedzieć. To staje się bardzo, bardzo złożone, a złożoność jest w zasadzie nieograniczona. Możesz nadal dodawać do swojego kompilatora warstwy wyrafinowania, ale zawsze będzie sposób na przemyślenie jakiegoś kodu, który będzie niedostępny dla dowolnego zestawu danych wejściowych.
Są też zestawy danych wejściowych, które po prostu nigdy nie są wprowadzane. Dane wejściowe, które nie miałyby sensu w prawdziwym życiu, lub zostałyby zablokowane przez logikę sprawdzania poprawności w innym miejscu. Kompilator nie może o nich wiedzieć.
Efektem końcowym jest to, że chociaż narzędzia programowe, o których wspominali inni, są niezwykle przydatne, nigdy nie dowiesz się na pewno, że złapałeś wszystko, chyba że później przejdziesz ręcznie kod. Nawet wtedy nigdy nie będziesz pewny, że niczego nie przegapiłeś.
Jedynym prawdziwym rozwiązaniem, IMHO, jest zachowanie jak największej czujności, wykorzystanie automatyzacji do dyspozycji, refaktoryzacja tam, gdzie to możliwe, i ciągłe poszukiwanie sposobów na ulepszenie kodu. Oczywiście i tak warto to zrobić.