8.3 — Typowe problemy z instrukcjami if

Ta lekcja jest kontynuacją lekcji 8.2 -- Instrukcje i bloki if. Podczas tej lekcji przyjrzymy się niektórym typowym problemom, które pojawiają się podczas używania instrukcji if.

Zagnieżdżone instrukcje if i problem wiszącego else

Możliwe jest zagnieżdżanie instrukcji if w innych instrukcjach if:

#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // outer if statement
        // it is bad coding style to nest if statements this way
        if (x <= 20) // inner if statement
            std::cout << x << " is between 0 and 20\n";

    return 0;
}

Rozważmy teraz następujący program:

#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // outer if-statement
        // it is bad coding style to nest if statements this way
        if (x <= 20) // inner if-statement
            std::cout << x << " is between 0 and 20\n";

    // which if statement does this else belong to?
    else
        std::cout << x << " is negative\n";

    return 0;
}

Powyższy program wprowadza źródło potencjalnej niejednoznaczności zwane zawieszenie else problem. Czy instrukcja else w powyższym programie jest dopasowana do zewnętrznej lub wewnętrznej instrukcji if?

Odpowiedź jest taka, że ​​instrukcja else jest sparowana z ostatnią niedopasowaną instrukcją if w tym samym bloku. Zatem w powyższym programie instrukcja else jest dopasowywana do wewnętrznej instrukcji if, tak jakby program został napisany w ten sposób:

#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // outer if statement
    {
        if (x <= 20) // inner if statement
            std::cout << x << " is between 0 and 20\n";
        else // attached to inner if statement
            std::cout << x << " is negative\n";
    }

    return 0;
}

To powoduje, że powyższy program generuje nieprawidłowe dane wyjściowe:

Enter a number: 21
21 is negative

Aby uniknąć takich niejasności podczas zagnieżdżania instrukcji if, dobrym pomysłem jest jawne zamknięcie wewnętrznej instrukcji if w bloku. Dzięki temu możemy bez dwuznaczności dołączyć instrukcję else do wewnętrznej lub zewnętrznej instrukcji if:

#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0)
    {
        if (x <= 20)
            std::cout << x << " is between 0 and 20\n";
        else // attached to inner if statement
            std::cout << x << " is greater than 20\n";
    }
    else // attached to outer if statement
        std::cout << x << " is negative\n";

    return 0;
}

Instrukcja else w bloku jest dołączana do wewnętrznej instrukcji if, a instrukcja else na zewnątrz bloku jest dołączana do zewnętrznej instrukcji if.

Spłaszczanie zagnieżdżonych instrukcji if

Zagnieżdżone instrukcje if mogą często być spłaszczane przez albo restrukturyzację logiki lub użycie operatorów logicznych (omówionych w lekcji 6.8 -- Operatory logiczne). Kod mniej zagnieżdżony jest mniej podatny na błędy.

Na przykład powyższy przykład można spłaszczyć w następujący sposób:

#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x < 0)
        std::cout << x << " is negative\n";
    else if (x <= 20) // only executes if x >= 0
        std::cout << x << " is between 0 and 20\n";
    else // only executes if x > 20
        std::cout << x << " is greater than 20\n";

    return 0;
}

Oto kolejny przykład użycia operatorów logicznych do sprawdzenia wielu warunków w ramach jednej instrukcji if:

#include <iostream>

int main()
{
    std::cout << "Enter an integer: ";
    int x{};
    std::cin >> x;

    std::cout << "Enter another integer: ";
    int y{};
    std::cin >> y;

    if (x > 0 && y > 0) // && is logical and -- checks if both conditions are true
        std::cout << "Both numbers are positive\n";
    else if (x > 0 || y > 0) // || is logical or -- checks if either condition is true
        std::cout << "One of the numbers is positive\n";
    else
        std::cout << "Neither number is positive\n";

    return 0;
}

Instrukcje null

A instrukcja null to instrukcje wyrażeń składające się tylko ze średnika:

if (x > 10)
    ; // this is a null statement

Instrukcje null nic nie robią. Są one zwykle używane, gdy język wymaga istnienia instrukcji, ale programista jej nie potrzebuje. Aby zapewnić czytelność, instrukcje null są zwykle umieszczane w osobnych wierszach. Przykłady zamierzonych instrukcji null zobaczymy w dalszej części tego rozdziału, kiedy będziemy omawiać pętle.

Instrukcje null rzadko są celowo używane z instrukcjami if. Mogą jednak w sposób niezamierzony sprawić problemy nowym (lub nieostrożnym) programistom. Rozważmy następujący fragment:

if (nuclearCodesActivated()); // note the semicolon at the end of this line
    blowUpTheWorld();

W powyższym fragmencie programista przypadkowo umieścił średnik na końcu instrukcji if (częsty błąd, ponieważ wiele instrukcji kończy się średnikami). Ten skromny błąd kompiluje się poprawnie i powoduje, że fragment kodu jest wykonywany tak, jakby został napisany w ten sposób:

if (nuclearCodesActivated())
    ; // the semicolon acts as a null statement
blowUpTheWorld(); // and this line always gets executed!

Ostrzeżenie

Uważaj, aby nie „zakończyć” instrukcji if średnikiem, w przeciwnym razie instrukcje, które chcesz wykonać warunkowo, zostaną zamiast tego wykonane bezwarunkowo (nawet jeśli znajdują się w bloku).

Wskazówka

W Pythonie słowo kluczowe pass służy jako instrukcja zerowa. Często jest używany jako symbol zastępczy dla kodu, który zostanie zaimplementowany później. Ponieważ jest to słowo, a nie symbol, pass jest mniej podatny na niezamierzone niewłaściwe użycie i łatwiejszy do przeszukiwania (co pozwala później łatwo znaleźć te symbole zastępcze).

for x in [0, 1, 2]:
  pass               # To be completed in the future

W C++ możemy naśladować pass używając preprocesora:

#define PASS

void foo(int x, int y)
{
    if (x > y)
        PASS;
    else
        PASS;
}

int main()
{
    foo(4, 7);

    return 0;
}

W celu zapewnienia spójności z innymi instrukcjami C++, nasze PASS wymaga zakończenia średnik. PASS jest usuwany przez preprocesor, a końcowy średnik jest interpretowany przez kompilator jako instrukcja zerowa.

Operator== vs Operator= wewnątrz warunku

Wewnątrz warunku, którego powinieneś użyć operator== podczas testowania równości, a nie operator= (co jest przypisaniem). Rozważmy następujący program:

#include <iostream>

int main()
{
    std::cout << "Enter 0 or 1: ";
    int x{};
    std::cin >> x;
    if (x = 0) // oops, we used an assignment here instead of a test for equality
        std::cout << "You entered 0\n";
    else
        std::cout << "You entered 1\n";

    return 0;
}

Ten program zostanie skompilowany i uruchomiony, ale w niektórych przypadkach da błędny wynik:

Enter 0 or 1: 0
You entered 1

W rzeczywistości ten program zawsze wygeneruje wynik You entered 1. Dzieje się tak, ponieważ x = 0 najpierw przypisuje wartość 0 Do x, następnie ocenia wartość x, która jest teraz 0, czyli Boolean false. Ponieważ warunek jest zawsze false, instrukcja else zawsze jest wykonywana.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
77 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze