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.
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.
Mbędzie to maksymalna wartość size_t? Czy nadal uważasz, że to sprytne?
#define TRUE FALSEi pojechać na wakacje.
Odpowiedzi:
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.
Mnie bycia std::numeric_limits<std::size_t>::max().
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.
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.)
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++;
}
0. Dlaczego nie for(size_t j = M; j-- != 0; )?
numeric_limits<size_t>::max().
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.
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.
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.