Co to jest „zwarcie” w językach podobnych do C?

14

Słyszałem o użyciu terminu „zwarcie” w językach C, C ++, C #, Java i wielu innych. Co to oznacza i w jakim scenariuszu zostałby zastosowany?

fasil
źródło
6
Istnieje artykuł Wikipedii na temat koncepcji: en.wikipedia.org/wiki/Short-circuit_evaluation Jest to optymalizacja oceny &&operatora.
wirrbel,
1
@wirrbel Myślę, że dotyczy to ||również ... przynajmniej powinno.
Radu Murzea
1
@RaduMurzea Rzeczywiście. Kontrast ||i &&do &i |zobaczyć subtelną różnicę. Mieć prosty program do oceny 1 || printf("yay");vs 0 || printf("yay");i 1 | printf("yay");vs, 0 | printf("yay");aby zobaczyć różnice
wirrbel

Odpowiedzi:

35

Zwarcie w C występuje, gdy operator logiczny nie ocenia wszystkich swoich argumentów.

Weźmy na przykład i &&, to oczywiste, że 0 && WhoCaresbędzie to fałsz bez względu na to, co WhoCaresjest. Z tego powodu C po prostu pomija ocenę WhoCares. To samo dotyczy 1 || WhoCares, zawsze będzie to prawdą. Z tego powodu możemy pisać jak kod

CanFireMissiles && FireMissiles()

W ten sposób unikamy wykonywania potencjalnie niemożliwych operacji. Jeśli nie możemy wystrzelić pocisków, z pewnością nie chcemy próbować. Jest to powszechnie używane ze wskaźnikami, zwłaszcza wskaźnikami plików.

 bool isN(int* ptr, int n){
     return ptr && *ptr == n;
 }

Działa to na wiele innych przydatnych sposobów, aby uniknąć niepotrzebnego przetwarzania

 isFileReady() || getFileReady()

Pozwala to uniknąć dodatkowej pracy, jeśli nie jest to konieczne.

Daniel Gratzer
źródło
1
W dowolnym momencie, jeśli odpowiedziałem na twoje pytanie, możesz zaznaczyć pole wyboru obok niego, aby zaznaczyć swoje pytanie jako odpowiedź
Daniel Gratzer,
7
Nie kocham CanFireMissiles && FireMissiles(), ponieważ podejrzewam, że nadużywacie zwarcia, aby wywołać skutki uboczne. Czuję, że ukrywasz działania w warunkach warunkowych. Taki kod lepiej napisać jako if(CanFireMissiles){FireMissiles();}lub if(CanFireMissles){didFireMissiles = TryFireMissiles(); if(didFireMissiles){...}}.
Brian
2
Twierdziłbym, że jedynym zastosowaniem jest ukrywanie efektów ubocznych. Zwykle nie jest to „wysadzanie miasta w powietrze”, ale takie rzeczy, jak dereferencjowanie wskaźnika lub korzystanie z zasobów systemowych, są często wykonywane w ten sposób w C. Zobacz stronę wikipedii, cała wykorzystywana sekcja to „Ukrywanie skutków ubocznych”
Daniel Gratzer
2
@ jozefg możesz go również użyć, aby zapobiec wykonywaniu kosztownych operacji, takich jak IsInCache(value) || IsInDatabase(value), gdzie IsInDatabase może zająć trochę czasu (zwłaszcza jeśli korzystanie z urządzenia mobilnego i opóźnienie sieci jest problemem).
mgw854,
4

„Zwarcie” zazwyczaj odnosi się do „ oceny zwarcia ”, która jest ogólną koncepcją, a nie tylko C.

Operatory logiczne oceniają od lewej do prawej, więc wszelkie warunki, które spowodują, że inne warunki będą niepotrzebne, będą przydatne. Możesz więc sprawdzić stan, który później wyklucza inne warunki, umożliwiając w ten sposób częściową ocenę operacji logicznych zamiast oceny całości.

Przykład:

while((x && y) == 1) {
    //This bit will not execute if x is 0 or y is 0 but y won't even be 
    //evaluated due to short circuit evaluation if x is 0.
}

Bardziej złożony przykład:

if((a || b || c || d || e || f || g || h || i || j || k) == 1) {
    /* If any of these are equal to 1 the whole expression is equal to 1,
     * thus doesn't it make sense to short circuit evaluate this?
     * Saves a bunch of time.
     */
}
Inżynier świata
źródło
8
Krótkie spięcie to nie tyle oszczędność czasu, co więcej brak oceny. Funkcja, która nie jest oceniana, również nie będzie miała skutku ubocznego.
Pieter B,
Wiesz, == 0nie tylko jest to niepotrzebne, ale może dezorientować niektórych ludzi.
Deduplicator,
3

Ocena krótkiego obwodu może prowadzić do tego, że niektóre części stanu nie będą oceniane.

Na przykład:

if (true || f()) { ... }

nie będzie f.

Szczery
źródło