Historia pytania:
Operatory logiczne && i || zostały wprowadzone w C do sprawdzania złożonych warunków logicznych. Cechą ich działania jest wsparcie dla obliczeń z krótkim zamknięciem: drugi operand nie jest obliczany, jeśli wynik można jednoznacznie określić już na podstawie pierwszego.
Problem:
Wielu programistów oczekuje, że oba operandy będą zawsze obliczane, lub niepoprawnie korzysta z efektów ubocznych w drugim operandi, zakładając, że na pewno zostanie wykonany. W praktyce prowadzi to do błędów, wycieków zasobów i zaskakującego zachowania.
Rozwiązanie:
Zrozumienie mechanizmu short-circuit evaluation pomaga w budowaniu bezpiecznych konstrukcji, szczególnie w kontroli wskaźników, zasobów i plików. Użycie efektów ubocznych w prawej części wyrażenia jest dozwolone tylko świadomie. Przykład bezpiecznej kontroli:
if (ptr && ptr->field) { /* ... */ }
Kluczowe cechy:
Czy wyrażenie f() zostanie wykonane w fragmencie: if (0 && f())
Nie, funkcja f() nie zostanie wywołana, ponieważ wynik jest już jasny — wyrażenie jest fałszywe, dalsze obliczenia są bezużyteczne.
A w następującym zapisie: if (1 || f())?
Ponownie f() nie zostanie wywołana: wynik jest już prawdziwy po pierwszym operandi.
Czy można używać operatorów && i || do kontrolowania kolejności wykonywania funkcji z efektami ubocznymi?
Technicznie tak, ale takie zarządzanie prowadzi do nieczytelnego i niestabilnego kodu. Lepiej jasno określić kolejność wywołań funkcji, nie polegając na zachowaniu short-circuit dla efektów ubocznych.
if (flag || process()) { // ... }
Funkcja nigdy się nie wywoła, jeśli flag jest prawdziwa.
Zalety:
Wady:
if (!flag) process();
Zalety:
Wady: