Pętla „for”, która wydaje się być praktycznie nieskończona

82

W tej chwili debuguję kod i natknąłem się na następującą linię:

for (std::size_t j = M; j <= M; --j)

(Napisane przez mojego szefa, który jest na wakacjach.)

Wydaje mi się to naprawdę dziwne.

Co to robi? Dla mnie wygląda jak nieskończona pętla.

Michael Bullock
źródło
31
size_tjest bez znaku, więc jest gwarantowane, że zawinie się do maksymalnej wartości podczas próby wycofania zera, kończąc pętlę. Wciąż jednak okropny kod.
BoBTFish
4
@Bathsheba A co, jeśli Mbędzie to maksymalna wartość size_t? Czy nadal uważasz, że to sprytne?
Thorsten Dittmar
8
@Barmar Niewiele się nie pojawia, nigdy się nie pojawia , więc istnieje prawdopodobieństwo> 0. Co oznacza, że ​​to rozwiązanie jest głupie, kiedy można to ująć w prosty sposób.
Thorsten Dittmar
38
Nie rozumiem obniżania głosów. To pytanie jest jasne - nie widzę, jak można by to poprawić. Od nowego użytkownika to pytanie jest naprawdę interesujące !
Batszeba
17
#define TRUE FALSEi pojechać na wakacje.
Leo

Odpowiedzi:

68

std::size_tjest gwarantowany przez standard C ++ jako unsignedtyp. A jeśli zmniejszysz unsignedtyp od 0, standard gwarantuje, że wynikiem tego będzie największa wartość dla tego typu.

Ta wartość zawinięcia jest zawsze większa lub równa M1 więc pętla się kończy.

Zatem j <= Mzastosowanie do unsignedtypu jest wygodnym sposobem powiedzenia „uruchom pętlę do zera, a następnie zatrzymaj”.

Istnieją alternatywy, takie jak uruchomienie jwiększego niż chcesz, a nawet użycie operatora slajdu for (std::size_t j = M + 1; j --> 0; ){ , które są prawdopodobnie bardziej przejrzyste, chociaż wymagają więcej pisania. Wydaje mi się jednak, że jedną wadą (poza oszałamiającym efektem, jaki wywołuje podczas pierwszej inspekcji) jest to, że nie przenosi się dobrze na języki bez typów bez znaku, takich jak Java.

Zauważ również, że schemat, który wybrał twój szef "pożycza" możliwą wartość z unsignedzestawu: tak się dzieje w tym przypadku, że Mustawienie na std::numeric_limits<std::size_t>::max()nie będzie działać poprawnie. W rzeczywistości w tym przypadku pętla jest nieskończona . (Czy to właśnie obserwujesz?) Powinieneś wstawić komentarz o tym skutku w kodzie, a może nawet potwierdzić ten konkretny warunek.


1 Z zastrzeżeniem Mnie bycia std::numeric_limits<std::size_t>::max().

Batszeba
źródło
7
Winię za pogodę.
Hatted Rooster
3
Jeśli M jest std::numeric_limits<std::size_t>::max()następnie M + 1będzie zerowy, a for (std::size_t j = M + 1; j --> 0; )pętla nie wykona pętlę w ogóle.
Pete Kirkham
51
Nie nazywaj tego „operatorem slajdów”. W C ++ nie ma takiego operatora, to po prostu sprytnie wyglądające zaciemnienie.
Ty
26
@DimitarMirchev: Ponieważ to nie jest operator. To dwa operatory z dziwnymi odstępami. Nazwij to idiomem, jeśli nalegasz na zadawanie ankietowanym nieistotnych pytań (ale najlepiej zapytaj ich, jak wdrożyliby tę funkcję, zamiast pytać o „sprytną” składnię).
Ty
1
Zakładając, że size_tjest to 64 bity, zaobserwowanie nieprawidłowego zachowania w przypadku krawędzi zajmie kilkaset lat. (Chyba że optymalizator może pozbyć się pętli.)
Carsten S,
27

To, co prawdopodobnie próbował zrobić twój szef, to odliczanie od Mdo zera włącznie, wykonując jakąś akcję na każdej liczbie.

Niestety istnieje skrajny przypadek, w którym rzeczywiście da ci to nieskończoną pętlę, taką, w której Mjest maksymalna size_twartość, jaką możesz mieć. I chociaż jest dobrze zdefiniowane, co zrobi wartość bez znaku, gdy zmniejszysz ją od zera, uważam, że sam kod jest przykładem niechlujnego myślenia, zwłaszcza, że ​​istnieje doskonale wykonalne rozwiązanie bez niedociągnięć twoich szefów.

Ten bezpieczniejszy wariant (i bardziej czytelny, moim zdaniem, przy zachowaniu wąskiego limitu zakresu), to:

{
    std::size_t j = M;
    do {
        doSomethingWith(j);
    } while (j-- != 0);
}

Jako przykład zobacz następujący kod:

#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
    uint32_t quant = 0;
    unsigned short us = USHRT_MAX;
    std::cout << "Starting at " << us;
    do {
        quant++;
    } while (us-- != 0);
    std::cout << ", we would loop " << quant << " times.\n";
    return 0;
}

Robi to w zasadzie to samo z an unsigned shorti widać, że przetwarza każdą pojedynczą wartość:

Starting at 65535, we would loop 65536 times.

Zastąpienie do..whilepętli w powyższym kodzie tym, co w zasadzie zrobił twój szef, spowoduje nieskończoną pętlę. Wypróbuj i zobacz:

for (unsigned int us2 = us; us2 <= us; --us2) {
    quant++;
}
paxdiablo
źródło
7
Teraz sprawa krawędzi jest 0. Dlaczego nie for(size_t j = M; j-- != 0; )?
LogicStuff
Tak, cierpi na tym, że przypadek narożny jest prawdopodobnie częstszy niż numeric_limits<size_t>::max().
Batszeba
7
@Logic et al, w podanej przeze mnie metodzie nie ma przypadków krawędzi, dodałem kod, aby to pokazać. Z proponowanego rozwiązania, początkową wartość M == 0spowoduje elementu 0 Nie przetwarzane, więc nie mają sprawę krawędzi. Korzystanie z mojej do..whilemetody post-check całkowicie eliminuje przypadek krawędzi. Jeśli spróbujesz tego z M == 1, zobaczysz, że robi zarówno 1, jak i 0. Podobnie, zacznij od max_size_t(cokolwiek to jest) i pomyślnie rozpocznie się w tym momencie, a następnie zmniejszy się do zera włącznie.
paxdiablo
Co ciekawe, ten przypadek motywuje do użycia czegoś, co niektórzy powiedzieliby, że jest „kodem niestrukturalnym”, ponieważ używa „ exitif”, a mianowicie { j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }. Może nawet zajść potrzeba zastąpienia breaka, gotojeśli rzeczy zostaną zagnieżdżone, ponieważ C nie ma nazwanych pętli. Jeśli „ustrukturyzowany” oznacza „podatny na wnioskowanie o fragmentach”, to jest ustrukturyzowany (układ pomaga!), A także jeśli oznacza „podatny na formalną walidację poprzez rozumowanie warunków wstępnych i końcowych”. Chociaż w tym przypadku j--działa, styl exitif może być uzasadniony, gdy wymagane jest bardziej skomplikowane przejście.
PJTraill
@PJTraill Nie mogę powiedzieć, że to, co mówisz, jest uporządkowane, a to, co mówisz, nie jest uporządkowane. Rozumowanie na temat do-while jest proste. Nie "używa" wyjścia nie bardziej niż, ani w żaden bardziej nieuporządkowany sposób, podczas gdy to robi.
philipxy