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) // zewnętrzna instrukcja if
// zagnieżdżanie instrukcji if w ten sposób jest złym stylem
if (x <= 20) // wewnętrzna instrukcja if
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) // zewnętrzna instrukcja if
// zagnieżdżanie instrukcji if w ten sposób jest złym stylem
if (x <= 20) // wewnętrzna instrukcja if
std::cout << x << " is between 0 and 20\n";
// do której instrukcji if należy to jeszcze?
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) // zewnętrzna instrukcja if
{
if (x <= 20) // wewnętrzna instrukcja if
std::cout << x << " is between 0 and 20\n";
else // dołączony do wewnętrznej instrukcji if
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 // dołączony do wewnętrznej instrukcji if
std::cout << x << " is greater than 20\n";
}
else // dołączony do zewnętrznej instrukcji if
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) // wykonuje się tylko wtedy, gdy x >= 0
std::cout << x << " is between 0 and 20\n";
else // wykonuje się tylko wtedy, gdy 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) // && jest logiczna i -- sprawdza, czy oba warunki są spełnione
std::cout << "Both numbers are positive\n";
else if (x > 0 || y > 0) // || jest logiczne lub -- sprawdza, czy którykolwiek z warunków jest prawdziwy
std::cout << "One of the numbers is positive\n";
else
std::cout << "Neither number is positive\n";
return 0;
}Instrukcje null
instrukcja null to instrukcje wyrażeń składające się tylko ze średnika:
if (x > 10)
; // to jest instrukcja nullInstrukcje 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()); // zwróć uwagę na średnik na końcu tej linii
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())
; // średnik pełni funkcję instrukcji zerowej
blowUpTheWorld(); // a ta linia zawsze zostanie wykonana!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) // ups, użyliśmy tutaj przypisania zamiast testu dla równość
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.

