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.
size_t
jest 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.M
będzie to maksymalna wartośćsize_t
? Czy nadal uważasz, że to sprytne?#define TRUE FALSE
i pojechać na wakacje.Odpowiedzi:
std::size_t
jest gwarantowany przez standard C ++ jakounsigned
typ. A jeśli zmniejszyszunsigned
typ 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
M
1 więc pętla się kończy.Zatem
j <= M
zastosowanie dounsigned
typu jest wygodnym sposobem powiedzenia „uruchom pętlę do zera, a następnie zatrzymaj”.Istnieją alternatywy, takie jak uruchomienie
j
większego niż chcesz, a nawet użycie operatora slajdufor (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
unsigned
zestawu: tak się dzieje w tym przypadku, żeM
ustawienie nastd::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
M
nie byciastd::numeric_limits<std::size_t>::max()
.źródło
std::numeric_limits<std::size_t>::max()
następnieM + 1
będzie zerowy, afor (std::size_t j = M + 1; j --> 0; )
pętla nie wykona pętlę w ogóle.size_t
jest 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
M
do 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
M
jest maksymalnasize_t
wartość, 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 short
i widać, że przetwarza każdą pojedynczą wartość:Starting at 65535, we would loop 65536 times.
Zastąpienie
do..while
pę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++; }
źródło
0
. Dlaczego niefor(size_t j = M; j-- != 0; )
?numeric_limits<size_t>::max()
.M == 0
spowoduje elementu 0 Nie przetwarzane, więc nie mają sprawę krawędzi. Korzystanie z mojejdo..while
metody post-check całkowicie eliminuje przypadek krawędzi. Jeśli spróbujesz tego zM == 1
, zobaczysz, że robi zarówno 1, jak i 0. Podobnie, zacznij odmax_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ąpieniabreak
a,goto
jeś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 przypadkuj--
działa, styl exitif może być uzasadniony, gdy wymagane jest bardziej skomplikowane przejście.