Po co programom rozgałęzienia
Każdy dotychczasowy program wykonywał się prosto od góry: linia 1, linia 2, linia 3. Prawdziwe programy podejmują decyzje. Czy hasło jest poprawne? Czy liczba jest ujemna? Czy plik się otworzył? Instrukcją if C zadaje pytanie i wykonuje kod tylko wtedy, gdy odpowiedź brzmi "tak".
Warunek temperature > 30 jest sprawdzany raz. Jest spełniony, więc komunikat się wypisuje. Ostatnia linia jest poza nawiasami klamrowymi, więc wypisuje się zawsze. Zmień 31 na 18 i uruchom ponownie: pierwszy komunikat znika, a drugi zostaje.
Składnia: nawiasy, klamry i brak średnika
if w C ma stały kształt:
if (condition) {
// wykonuje się, gdy warunek jest niezerowy
}
Trzy zasady warto zapamiętać od razu. Nawiasy wokół warunku są obowiązkowe. Nie ma słowa kluczowego then. I nie ma średnika po nawiasie zamykającym: zabłąkany średnik kończy instrukcję właśnie w tym miejscu:
// BŁĄD: if nic nie robi; printf wykonuje się zawsze
if (x > 10);
{
printf("duze\n");
}
To się kompiluje bez zastrzeżeń. ; to pusta instrukcja, która staje się ciałem if, a klamry pod nią to po prostu samodzielny blok, który wykonuje się zawsze. Kompilacja z gcc -Wall to wyłapie.
Same klamry są opcjonalne przy pojedynczej instrukcji, ale je zostaw. if bez klamer, do którego później dochodzi druga linia, to klasyczne źródło błędów, bo w C wcięcia nie grupują instrukcji.
else i else if
else daje drugą ścieżkę:
Gdy wyników jest więcej niż dwa, połącz je w łańcuch przez else if. C sprawdza warunki po kolei i zatrzymuje się na pierwszym spełnionym:
Kolejność ma tu ogromne znaczenie. Łańcuch zatrzymuje się na pierwszym dopasowaniu, więc drugi test widzi tylko wyniki poniżej 90 i dlatego score >= 80 nie musi dodatkowo mówić && score < 90. Pisz łańcuch od warunku najbardziej restrykcyjnego do najmniej restrykcyjnego, bo inaczej każda wartość wpadnie do pierwszej gałęzi.
Końcowe else jest opcjonalne. Dodawaj je zawsze, gdy łańcuch ma obejmować każdy przypadek; jego brak oznacza "jeśli nic nie pasuje, nie rób nic", co jest w porządku, ale powinno być świadomą decyzją.
Prawdziwość: 0 to fałsz, wszystko inne to prawda
Podstawowy język C nie ma w warunkach osobnego typu logicznego. if po prostu pyta, czy wyrażenie jest niezerowe.
Ostatnie dwie linie pokazują drugą połowę reguły: operatory porównania to zwykłe wyrażenia, które dają 1 dla prawdy i 0 dla fałszu. Dlatego if (5 > 2) działa: to tak naprawdę if (1).
Z tego samego powodu -3 jest w C prawdą. Reguła brzmi niezerowe, a nie dodatnie. Jeśli chcesz wiedzieć, czy liczba jest dodatnia, napisz if (n > 0), a nie if (n).
C99 dodało <stdbool.h> z prawdziwym typem bool oraz true i false, co lepiej się czyta w nowym kodzie. Pod spodem to nadal 1 i 0, więc powyższa reguła się nie zmienia; więcej na stronie o wartościach logicznych w C.
Operatory porównania i logiczne, których będziesz używać w warunkach:
== równe && logiczne AND (obie strony prawdziwe)
!= różne || logiczne OR (któraś strona prawdziwa)
< mniejsze niż ! logiczne NOT
> większe niż
<= mniejsze lub równe
>= większe lub równe
&& i || działają skrótowo: a && b nigdy nie oblicza b, jeśli a jest fałszem, a a || b nigdy nie oblicza b, jeśli a jest prawdą. To gwarancja, a nie optymalizacja, więc można bezpiecznie napisać if (n != 0 && total / n > 5): dzielenie nie wykona się, gdy n to zero.
Błąd = zamiast ==
To najczęstszy błąd w warunkach w C:
// BŁĄD: przypisuje 5 do x, potem testuje 5 (niezerowe), więc to wykonuje się ZAWSZE
if (x = 5) {
printf("x to piec\n");
}
C pozwala na przypisanie wewnątrz wyrażenia, więc ta linia jest poprawna. Zapisuje 5 w x, daje wartość 5, a 5 jest niezerowe: gałąź wykonuje się za każdym razem, a x zostało po cichu nadpisane.
Są dwie obrony. Po pierwsze, zawsze kompiluj z włączonymi ostrzeżeniami:
gcc -Wall -Wextra program.c -o program
GCC i clang zgłaszają wtedy "suggest parentheses around assignment used as truth value". Po drugie, niektórzy programiści piszą stałą przed zmienną, if (5 == x), żeby literówka if (5 = x) była twardym błędem kompilacji, a nie cichym błędem w działaniu. Ten styl to kwestia gustu; włączenie ostrzeżeń już nie.
Zagnieżdżanie
if może zawierać kolejny if. Zagnieżdżenie wyraża warunki, które mają sens dopiero wtedy, gdy wcześniejszy jest spełniony:
Dwa poziomy są czytelne. Cztery już nie. Gdy zagnieżdżenie się pogłębia, poszukaj sposobu na jego spłaszczenie: często przez połączenie warunków za pomocą && albo obsłużenie najpierw przypadków błędnych i wczesny powrót:
// płasko: najpierw wyjątki, potem właściwa praca
if (age < 18) { printf("Tylko dla doroslych.\n"); return 0; }
if (!hasTicket) { printf("Prosimy kupic bilet.\n"); return 0; }
printf("Zapraszamy.\n");
Wiszące else
Gdy if bez klamer jest zagnieżdżony w innym, do którego if należy else? Reguła C: else wiąże się z najbliższym niesparowanym if, niezależnie od wcięć.
// else należy do WEWNĘTRZNEGO if, mimo wcięcia
if (a > 0)
if (b > 0)
printf("obie dodatnie\n");
else
printf("a nie jest dodatnie\n"); // ŹLE: wykonuje się, gdy a > 0 i b <= 0
Wcięcie sugeruje, że else łączy się z zewnętrznym if. Kompilator łączy je z wewnętrznym. Klamry całkowicie usuwają tę niejednoznaczność i to jest prawdziwy argument za tym, żeby używać ich zawsze:
if (a > 0) {
if (b > 0) {
printf("obie dodatnie\n");
}
} else {
printf("a nie jest dodatnie\n");
}
Operator trójargumentowy
Gdy gałąź istnieje tylko po to, żeby wybrać wartość, operator warunkowy ? : wyraża to jednym wyrażeniem:
Czytaj cond ? x : y jako "jeśli cond, to x, w przeciwnym razie y". Obliczana jest dokładnie jedna z dwóch gałęzi, a całość daje wartość i dlatego można ją przypisać albo przekazać jako argument tam, gdzie instrukcja by nie pasowała.
Zostaw go do prostych wyborów wartości. Zagnieżdżone operatory trójargumentowe (a ? b : c ? d : e) są poprawne, ale prawie zawsze trudniej je czytać niż łańcuch else if, który zastępują.
Najczęściej zadawane pytania
Jak napisać instrukcję if else w C?
Warunek umieść w nawiasach okrągłych, a kod w nawiasach klamrowych: if (score >= 60) { printf("Pass\n"); } else { printf("Fail\n"); }. Blok else wykonuje się tylko wtedy, gdy warunek jest fałszywy, a w C nie ma słowa kluczowego then.
Co w instrukcji if w C liczy się jako prawda?
Każda wartość różna od zera. Podstawowa składnia C nie ma osobnego typu logicznego, więc if (n) wykona się, gdy n jest czymkolwiek poza 0, a if (!n) tylko wtedy, gdy n to dokładnie 0. Operatory porównania, takie jak == i <, dają 1 dla prawdy i 0 dla fałszu.
Czym różni się = od == w C?
== porównuje, = przypisuje. if (x = 5) przypisuje 5 do x, a potem testuje 5, które jest niezerowe, więc gałąź wykonuje się zawsze. Kompiluje się to bez błędu i dlatego to najczęstszy błąd początkujących w warunkach w C. Większość kompilatorów ostrzega o nim przy -Wall.
Jak działa operator trójargumentowy w C?
condition ? valueIfTrue : valueIfFalse to wyrażenie, które daje jedną z dwóch wartości, więc może stać w przypisaniu albo w argumencie printf: int max = a > b ? a : b;. Używaj go do wyboru wartości, a zwykłego if, gdy trzeba wykonać instrukcje.