Menu

If else w C: składnia, przykłady i częste błędy

Jak C podejmuje decyzje: instrukcja if, else, łańcuchy else if, zagnieżdżanie i operator trójargumentowy, a do tego reguła prawdziwości (0 to fałsz, wszystko inne to prawda) i błąd = zamiast ==, który po cichu psuje warunki.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ