W przewodniku Google C ++ Style Guide na temat „Unsigned Integers” jest to sugerowane
Ze względu na przypadek historyczny standard C ++ używa również liczb całkowitych bez znaku do reprezentowania rozmiaru kontenerów - wielu członków organizacji normalizacyjnej uważa, że jest to błąd, ale na tym etapie praktycznie nie można tego naprawić. Fakt, że arytmetyka bez znaku nie modeluje zachowania prostej liczby całkowitej, ale jest definiowana przez standard modelowania arytmetyki modularnej (zawijanie po przepełnieniu / niedomiarze) oznacza, że kompilator nie może zdiagnozować znaczącej klasy błędów.
Co jest nie tak z arytmetyką modularną? Czy nie jest to oczekiwane zachowanie niepodpisanego int?
Jakiego rodzaju błędów (znaczącej klasy) dotyczy przewodnik? Przepełnione błędy?
Nie używaj typu bez znaku tylko po to, aby zapewnić, że zmienna jest nieujemna.
Jednym z powodów, dla których przychodzi mi do głowy użycie signed int zamiast unsigned int, jest to, że jeśli przepełnia (do wartości ujemnej), łatwiej jest go wykryć.
źródło
unsigned int x = 0; --x;
i zobacz, cox
się stanie. Bez kontroli limitów rozmiar może nagle uzyskać nieoczekiwaną wartość, która może łatwo doprowadzić do UB.int
przepełnienie i niedomiar to UB. Jest mniej prawdopodobne, że doświadczysz sytuacji, w której aint
spróbuje wyrazić wartość, której nie może, niż sytuacja, która zmniejsza się ounsigned int
wartość poniżej zera, ale ludzie, którzy byliby zaskoczeni zachowaniemunsigned int
arytmetyki, to ludzie, którzy również mogą napisz kod, który spowodowałbyint
przepełnienie związane z UB, takie jak użyciea < a + 1
do sprawdzania przepełnienia.Odpowiedzi:
Niektóre odpowiedzi tutaj wspomnieć zaskakujące zasady promocji między podpisane i niepodpisane wartości, ale to wydaje się raczej problemem dotyczącym mieszania podpisane i niepodpisane wartości, a nie koniecznie wyjaśnić, dlaczego podpisane zmienne byłyby korzystniejsze niż unsigned zewnątrz mieszania scenariuszy.
Z mojego doświadczenia wynika, że poza mieszanymi porównaniami i zasadami promocji istnieją dwa główne powody, dla których wartości bez znaku są magnesami na błędy.
Wartości bez znaku mają nieciągłość na poziomie zero, najczęściej spotykaną wartość w programowaniu
Zarówno liczby całkowite bez znaku, jak i ze znakiem mają nieciągłości na swoich minimalnych i maksymalnych wartościach, gdzie zawijają się (bez znaku) lub powodują niezdefiniowane zachowanie (ze znakiem). Dla
unsigned
tych punktów są na zero iUINT_MAX
. Dlaint
są naINT_MIN
iINT_MAX
. Typowe wartościINT_MIN
iINT_MAX
w systemie z 4-bajtowymiint
wartościami to-2^31
i2^31-1
, aw takim systemieUINT_MAX
zazwyczaj jest2^32-1
.Podstawowym problemem wywołującym błąd
unsigned
, który nie dotyczy tegoint
jest to, że ma nieciągłość na poziomie zero . Zero jest oczywiście bardzo powszechną wartością w programach, wraz z innymi małymi wartościami, takimi jak 1,2,3. Powszechne jest dodawanie i odejmowanie małych wartości, zwłaszcza 1, w różnych konstrukcjach, a jeśli odejmiesz cokolwiek odunsigned
wartości i zdarzy się, że jest to zero, otrzymujesz ogromną wartość dodatnią i prawie pewien błąd.Rozważmy, że kod iteruje po wszystkich wartościach w wektorze według indeksu z wyjątkiem ostatniej 0,5 :
for (size_t i = 0; i < v.size() - 1; i++) { // do something }
Działa to dobrze, dopóki pewnego dnia nie przejdziesz do pustego wektora. Zamiast wykonywać zerowe iteracje, otrzymasz
v.size() - 1 == a giant number
1, a wykonasz 4 miliardy iteracji i prawie masz lukę przepełnienia bufora.Musisz to napisać tak:
for (size_t i = 0; i + 1 < v.size(); i++) { // do something }
Można więc to „naprawić” w tym przypadku, ale tylko poprzez dokładne przemyślenie niepodpisanej natury
size_t
. Czasami nie możesz zastosować powyższej poprawki ponieważ zamiast stałej masz jakąś zmienną offset, którą chcesz zastosować, która może być dodatnia lub ujemna: więc po której "stronie" porównania musisz je umieścić zależy od podpisu - teraz kod robi się naprawdę nieuporządkowany.Podobny problem występuje z kodem, który próbuje iterować w dół do zera włącznie. Coś jak
while (index-- > 0)
działa dobrze, ale pozornie odpowiednikwhile (--index >= 0)
nigdy nie kończy się dla wartości bez znaku. Twój kompilator może Cię ostrzec, gdy po prawej stronie jest dosłownie zero, ale z pewnością nie, jeśli jest to wartość określona w czasie wykonywania.Kontrapunkt
Niektórzy mogą twierdzić, że wartości ze znakiem również mają dwie nieciągłości, więc po co wybierać bez znaku? Różnica polega na tym, że obie nieciągłości są bardzo (maksymalnie) dalekie od zera. Naprawdę uważam to za osobny problem „przepełnienia”, zarówno wartości ze znakiem, jak i bez znaku mogą przepełniać się przy bardzo dużych wartościach. W wielu przypadkach przepełnienie jest niemożliwe ze względu na ograniczenia możliwego zakresu wartości, a przepełnienie wielu 64-bitowych wartości może być fizycznie niemożliwe). Nawet jeśli to możliwe, prawdopodobieństwo wystąpienia błędu związanego z przepełnieniem jest często znikome w porównaniu z błędem „przy zera”, a przepełnienie występuje również w przypadku wartości bez znaku . Tak więc bez znaku łączy w sobie to, co najgorsze z obu światów: potencjalne przepełnienie z bardzo dużymi wartościami wielkości i nieciągłość na poziomie zera. Podpisany ma tylko ten pierwszy.
Wielu będzie argumentować, że „trochę tracisz” przy braku znaku. Często jest to prawdą - ale nie zawsze (jeśli chcesz przedstawić różnice między wartościami bez znaku, i tak stracisz ten bit: tak wiele 32-bitowych rzeczy i tak jest ograniczonych do 2 GiB lub będziesz mieć dziwną szarą strefę, w której powiedz plik może mieć 4 GiB, ale nie można używać niektórych interfejsów API na drugiej połowie 2 GiB).
Nawet w przypadkach, gdy niepodpisany kupuje trochę: to niewiele: gdybyś musiał obsługiwać więcej niż 2 miliardy „rzeczy”, prawdopodobnie wkrótce będziesz musiał wesprzeć ponad 4 miliardy.
Logicznie rzecz biorąc, wartości bez znaku są podzbiorem wartości ze znakiem
Matematycznie, wartości bez znaku (nieujemne liczby całkowite) są podzbiorem liczb całkowitych ze znakiem (zwanych po prostu _całkami). 2 . Jeszcze podpisane wartości naturalnie wyskoczyć operacji wyłącznie na niepodpisanych wartości, takich jak odejmowania. Można powiedzieć, że wartości bez znaku nie są zamknięte przy odejmowaniu. To samo nie dotyczy wartości ze znakiem.
Chcesz znaleźć „różnicę” między dwoma niepodpisanymi indeksami w pliku? Cóż, lepiej wykonaj odejmowanie we właściwej kolejności, bo inaczej otrzymasz złą odpowiedź. Oczywiście często potrzebujesz sprawdzenia działania, aby określić właściwą kolejność! Gdy mamy do czynienia z wartościami bez znaku jako liczbami, często stwierdzamy, że (logicznie) podpisane wartości i tak pojawiają się, więc równie dobrze można zacząć od znaku ze znakiem.
Kontrapunkt
Jak wspomniano w przypisie (2) powyżej, podpisane wartości w C ++ nie są w rzeczywistości podzbiorem wartości bez znaku o tym samym rozmiarze, więc wartości bez znaku mogą reprezentować taką samą liczbę wyników, jak wartości ze znakiem.
To prawda, ale zakres jest mniej przydatny. Rozważ odejmowanie i liczby bez znaku w zakresie od 0 do 2N oraz liczby ze znakiem w zakresie od -N do N. Arbitralne odejmowania dają wyniki w zakresie od -2N do 2N w _w obu przypadkach, a każdy typ liczb całkowitych może reprezentować tylko połowa tego. Okazuje się, że region skupiony wokół zera od -N do N jest zwykle dużo bardziej przydatny (zawiera więcej rzeczywistych wyników w kodzie świata rzeczywistego) niż zakres od 0 do 2 N. Rozważ dowolny typowy rozkład inny niż jednorodny (log, zipfian, normalny, cokolwiek) i rozważ odjęcie losowo wybranych wartości z tego rozkładu: o wiele więcej wartości kończy się w [-N, N] niż [0, 2N] (w istocie, wynikowy rozkład jest zawsze wyśrodkowany na zero).
64-bit zamyka drzwi z wielu powodów, dla których warto używać wartości ze znakiem jako liczb
Myślę, że powyższe argumenty były już przekonujące dla wartości 32-bitowych, ale przypadki przepełnienia, które wpływają zarówno na podpisane, jak i niepodpisane przy różnych progach, tak występuje dla wartości 32-bitowych, ponieważ „2000000000” to numer, który może przekroczona o wiele wielkości abstrakcyjne i fizyczne (miliardy dolarów, miliardy nanosekund, tablice z miliardami elementów). Więc jeśli ktoś jest wystarczająco przekonany przez podwojenie dodatniego zakresu dla wartości bez znaku, może udowodnić, że przepełnienie ma znaczenie i nieco faworyzuje brak znaku.
Poza wyspecjalizowanymi domenami 64-bitowe wartości w dużej mierze eliminują ten problem. Podpisane wartości 64-bitowe mają górny zakres 9 223 372 036 854 775 807 - ponad dziewięć trylionów . To dużo nanosekund (około 292 lat) i dużo pieniędzy. Jest to również większa tablica niż jakikolwiek komputer, który prawdopodobnie będzie miał pamięć RAM w spójnej przestrzeni adresowej przez długi czas. Więc może 9 kwintylionów wystarczy każdemu (na razie)?
Kiedy używać wartości bez znaku
Zwróć uwagę, że przewodnik po stylach nie zabrania ani nawet nie odradza używania liczb bez znaku. Kończy się:
Rzeczywiście, zmienne bez znaku mają dobre zastosowania:
Gdy chcesz traktować liczbę N-bitową nie jako liczbę całkowitą, ale po prostu jako „worek bitów”. Na przykład jako maska bitowa lub mapa bitowa lub N wartości logicznych lub cokolwiek innego. To zastosowanie często idzie w parze z typami o stałej szerokości, takimi jak
uint32_t
i,uint64_t
ponieważ często chcesz znać dokładny rozmiar zmiennej. Wskazówką, że dana zmienna zasługuje na to leczenie jest to, że działają tylko na nim z bitowe operatorów takich jak~
,|
,&
,^
,>>
i tak dalej, a nie z operacji arytmetycznych, takich jak+
,-
,*
,/
etc.Bez znaku jest tutaj idealne, ponieważ zachowanie operatorów bitowych jest dobrze zdefiniowane i znormalizowane. Podpisane wartości mają kilka problemów, takich jak niezdefiniowane i nieokreślone zachowanie podczas przesuwania oraz nieokreślona reprezentacja.
Kiedy faktycznie potrzebujesz arytmetyki modularnej. Czasami faktycznie potrzebujesz arytmetyki modularnej 2 ^ N. W takich przypadkach „przepełnienie” jest funkcją, a nie błędem. Wartości bez znaku dają ci to, czego chcesz, ponieważ są zdefiniowane do używania arytmetyki modularnej. Podpisanych wartości nie można w ogóle (łatwo i wydajnie) wykorzystać, ponieważ mają one nieokreśloną reprezentację, a przepełnienie jest niezdefiniowane.
0.5 Po napisaniu tego zdałem sobie sprawę, że jest to prawie identyczne z przykładem Jaroda , którego nie widziałem - i nie bez powodu jest to dobry przykład!
1 Mówimy
size_t
tutaj, więc zwykle 2 ^ 32-1 w systemie 32-bitowym lub 2 ^ 64-1 w systemie 64-bitowym.2 W C ++ tak nie jest, ponieważ wartości bez znaku zawierają więcej wartości na górnym końcu niż odpowiadający im typ ze znakiem, ale istnieje podstawowy problem polegający na tym, że manipulowanie wartościami bez znaku może skutkować (logicznie) podpisanymi wartościami, ale nie ma odpowiedniego problemu z wartościami ze znakiem (ponieważ podpisane wartości zawierają już wartości bez znaku).
źródło
-ftrapv
które mogą przechwytywać wszystkie przepełnienia ze znakiem, ale nie wszystkie przepełnienia bez znaku. Wpływ na wydajność nie jest taki zły, więc-ftrapv
w niektórych scenariuszach może być rozsądna kompilacja .That's about the age of the universe measured in nanoseconds.
Wątpię w to. Wszechświat jest o13.7*10^9 years
starym, który jest4.32*10^17 s
lub4.32*10^26 ns
. Aby reprezentować4.32*10^26
jako int, potrzebujesz przynajmniej90 bits
.9,223,372,036,854,775,807 ns
tylko o292.5 years
.Jak wspomniano, mieszanie
unsigned
isigned
może prowadzić do nieoczekiwanego zachowania (nawet jeśli jest dobrze zdefiniowane).Załóżmy, że chcesz iterować po wszystkich elementach wektora z wyjątkiem ostatnich pięciu, możesz niepoprawnie napisać:
for (int i = 0; i < v.size() - 5; ++i) { foo(v[i]); } // Incorrect // for (int i = 0; i + 5 < v.size(); ++i) { foo(v[i]); } // Correct
Załóżmy
v.size() < 5
więc, jakv.size()
jestunsigned
,s.size() - 5
będzie to bardzo duża liczba, a więci < v.size() - 5
byłobytrue
bardziej oczekiwanym zakresie wartościi
. A UB dzieje się wtedy szybko (raz poza zasięgiemi >= v.size()
)Jeśli
v.size()
zwróciłby wartość ze znakiem, tos.size() - 5
byłby ujemny, aw powyższym przypadku warunek byłby natychmiast fałszywy.Z drugiej strony indeks powinien znajdować się między,
[0; v.size()[
więcunsigned
ma sens. Signed ma również swój własny problem jako UB z przepełnieniem lub zachowaniem zdefiniowanym przez implementację dla przesunięcia w prawo ujemnej liczby ze znakiem, ale rzadszym źródłem błędów dla iteracji.źródło
i<size()-X
należy pisaći+X<size()
. Jasne, trzeba o tym pamiętać, ale moim zdaniem nie jest to takie trudne.Jednym z najbardziej niepokojących przykładów błędu jest MIESZANIE wartości ze znakiem i bez znaku:
#include <iostream> int main() { auto qualifier = -1 < 1u ? "makes" : "does not make"; std::cout << "The world " << qualifier << " sense" << std::endl; }
Wyjście:
Świat nie ma sensu
Jeśli nie masz trywialnej aplikacji, nieuniknione jest, że skończysz z niebezpiecznymi mieszankami między wartościami podpisanymi i niepodpisanymi (powodującymi błędy w czasie wykonywania) lub jeśli włączysz ostrzeżenia i zrobisz z nich błędy w czasie kompilacji, skończysz z wieloma static_casts w Twoim kodzie. Dlatego najlepiej jest używać wyłącznie liczb całkowitych ze znakiem dla typów do matematycznego lub logicznego porównania. Używaj tylko bez znaku dla masek bitowych i typów reprezentujących bity.
Modelowanie typu bez znaku w oparciu o oczekiwaną dziedzinę wartości twoich liczb jest złym pomysłem. Większość liczb jest bliżej 0 niż 2 miliardów, więc w przypadku typów bez znaku wiele wartości jest bliżej krawędzi prawidłowego zakresu. Co gorsza, ostateczna wartość może znajdować się w znanym dodatnim zakresie, ale podczas obliczania wyrażeń wartości pośrednie mogą być niedostateczne i jeśli są używane w postaci pośredniej, mogą być BARDZO błędnymi wartościami. Wreszcie, nawet jeśli oczekuje się, że wartości zawsze będą dodatnie, nie oznacza to, że nie będą one oddziaływać z innymi zmiennymi, które mogą być ujemne, i kończy się to wymuszoną sytuacją mieszania typów ze znakiem i bez znaku, czyli najgorsze miejsce.
źródło
Użycie typu bez znaku nie powoduje większych błędów niż użycie typu podpisanego z pewnymi klasami zadań.
Użyj odpowiedniego narzędzia do pracy.
Jeśli zadanie jest dobrze dopasowane: nic złego. Nie, raczej nie.
Algorytm bezpieczeństwa, szyfrowania i uwierzytelniania opiera się na niepodpisanej modułowej matematyce.
Algorytmy kompresji / dekompresji, a także różne formaty graficzne przynoszą korzyści i są mniej błędne dzięki niepodpisanej matematyce.
Za każdym razem, operatory bitowe mądry i przesunięcia są używane, niepodpisane operacje nie się pokręcić się problematyką znak rozciągnięcia podpisanej matematyki.
Podpisana matematyka liczb całkowitych ma intuicyjny wygląd i jest łatwo zrozumiała dla wszystkich, w tym dla osób uczących się kodowania. C / C ++ nie był pierwotnie celem, ani teraz nie powinien być językiem wprowadzającym. Do szybkiego kodowania wykorzystującego siatki zabezpieczające przed przepełnieniem lepiej nadają się inne języki. W przypadku szybkiego kodu Lean C zakłada, że programiści wiedzą, co robią (mają doświadczenie).
Dzisiejszą pułapką matematyki podpisanej jest wszechobecna wersja 32-bitowa,
int
która przy tak wielu problemach jest wystarczająco szeroka dla typowych zadań bez sprawdzania zakresu. Prowadzi to do samozadowolenia, że przepełnienie nie jest kodowane. Zamiast tegofor (int i=0; i < n; i++)
int len = strlen(s);
jest postrzegany jako OK, ponieważn
zakłada się, że <INT_MAX
i ciągi nigdy nie będą zbyt długie, zamiast być chronione w pełnym zakresie w pierwszym przypadku lub przy użyciusize_t
,unsigned
a nawetlong long
w drugim.C / C ++ opracowany w erze, która obejmowała zarówno 16-bitowe, jak i 32-bitowe,
int
a dodatkowy bit, który zapewnia 16-bitowy bez znaku,size_t
był znaczący. Trzeba było zwrócić uwagę na problemy z przepełnieniem,int
czy to lubunsigned
.Przy 32-bitowych (lub szerszych) aplikacjach Google na
int/unsigned
platformach innych niż 16-bitowe , daje brak uwagi na +/- przepełnienie zint
uwagi na jego duży zasięg. Ma to sens do takich zastosowań, aby zachęcićint
ponadunsigned
. Jednakint
matematyka nie jest dobrze chroniona.Wąskie 16-bitowe
int/unsigned
problemy dotyczą obecnie wybranych aplikacji wbudowanych.Wytyczne Google mają zastosowanie do kodu, który piszą dzisiaj. Nie jest to ostateczna wytyczna dla szerszego zakresu kodu C / C ++.
W C / C ++ przepełnienie matematyczne ze znakiem int jest niezdefiniowanym zachowaniem, a więc z pewnością nie jest łatwiejsze do wykrycia niż zdefiniowane zachowanie matematyki bez znaku .
Jak dobrze skomentował @Chris Uzdavinis , mieszanie podpisów i niepodpisów jest najlepiej unikane przez wszystkich (szczególnie początkujących) i w inny sposób ostrożnie kodowane w razie potrzeby.
źródło
int
nie modeluje również zachowania „rzeczywistej” liczby całkowitej. Niezdefiniowane zachowanie przy przepełnieniu nie jest sposobem, w jaki matematyk myśli o liczbach całkowitych: nie ma możliwości „przepełnienia” abstrakcyjną liczbą całkowitą. Ale to są jednostki magazynowe maszyn, a nie liczby matematyka.signed int
przepełnienie jest łatwe do wykrycia (za pomocą-ftrapv
), podczas gdy niepodpisane „przepełnienie” jest trudne do wykrycia.Mam pewne doświadczenie z przewodnikiem stylistycznym Google, znanym również jako Przewodnik autostopowicza po szalonych dyrektywach od złych programistów, którzy dostali się do firmy dawno temu. Ta konkretna wskazówka jest tylko jednym z przykładów dziesiątek szalonych zasad w tej książce.
Błędy występują tylko w przypadku typów bez znaku, jeśli spróbujesz wykonać z nimi operacje arytmetyczne (zobacz przykład Chrisa Uzdavinisa powyżej), innymi słowy, jeśli używasz ich jako liczb. Typy bez znaku nie są przeznaczone do przechowywania ilości liczbowych, służą do przechowywania zliczeń, takich jak rozmiar kontenerów, które nigdy nie mogą być ujemne i mogą i powinny być używane do tego celu.
Pomysł wykorzystania typów arytmetycznych (takich jak liczby całkowite ze znakiem) do przechowywania rozmiarów kontenerów jest idiotyczny. Czy użyłbyś podwójnego do przechowywania rozmiaru listy? To, że w Google są ludzie, którzy przechowują rozmiary kontenerów przy użyciu typów arytmetycznych i wymagają od innych, aby robili to samo, mówi coś o firmie. Jedną rzeczą, którą zauważyłem w przypadku takich nakazów, jest to, że im są głupsi, tym bardziej muszą być surowymi regułami typu „zrób to albo jesteś zwolniony”, ponieważ w przeciwnym razie ludzie o zdrowym rozsądku zignorowaliby tę regułę.
źródło
unsigned
typy mogły przechowywać tylko liczby i nie byłyby używane w arytmetyce. Więc część „Szalone dyrektywy od złych programistów” ma więcej sensu.int
było 16 bitów, ale o wiele mniej dzisiaj), lepiej mieć liczniki, które zachowują się jak liczby.Używanie typów bez znaku do reprezentowania wartości nieujemnych ...
Wytyczne Google dotyczące kodowania kładą nacisk na pierwszy rodzaj rozważań. Inne zestawy wytycznych, takie jak C ++ Core Guidelines , kładą większy nacisk na drugą kwestię. Weźmy na przykład pod uwagę Podstawową wytyczną I.12 :
Oczywiście można argumentować za
non_negative
opakowaniem dla liczb całkowitych, który pozwala uniknąć obu kategorii błędów, ale miałoby to swoje własne problemy ...źródło
Oświadczenie Google dotyczy używania unsigned jako typu rozmiaru dla kontenerów . Natomiast pytanie wydaje się bardziej ogólne. Pamiętaj o tym podczas czytania.
Ponieważ większość dotychczasowych odpowiedzi reagowała na stwierdzenie google, a mniej na większe pytanie, zacznę od odpowiedzi o ujemnych rozmiarach pojemników, a następnie spróbuję przekonać każdego (beznadziejnego, wiem ...), że brak znaku jest dobry.
Podpisane rozmiary kontenerów
Załóżmy, że ktoś zakodował błąd, którego wynikiem jest ujemny indeks kontenera. Rezultatem jest niezdefiniowane zachowanie lub wyjątek / naruszenie zasad dostępu. Czy to naprawdę lepsze niż uzyskanie niezdefiniowanego zachowania lub naruszenia wyjątku / dostępu, gdy typ indeksu był niepodpisany? Myśle że nie.
Jest klasa ludzi, którzy uwielbiają rozmawiać o matematyce i tym, co w tym kontekście jest „naturalne”. W jaki sposób typ całkowy z liczbą ujemną może być naturalny do opisania czegoś, co jest z natury> = 0? Często używasz tablic o ujemnych rozmiarach? IMHO, zwłaszcza ludzie o skłonnościach matematycznych, uznaliby tę niedopasowanie semantyki (typ rozmiaru / indeksu mówi, że jest możliwy negatyw, podczas gdy tablica o rozmiarze ujemnym jest trudna do wyobrażenia) irytująca.
Pozostaje więc tylko pytanie, czy - jak stwierdzono w komentarzu google - kompilator rzeczywiście mógłby aktywnie pomagać w znajdowaniu takich błędów. I nawet lepiej niż alternatywa, która byłaby chronionymi niedopływem liczb całkowitych bez znaku (zestaw x86-64 i prawdopodobnie inne architektury mają środki do osiągnięcia tego, tylko C / C ++ nie używa tych środków). Jedynym sposobem, w jaki mogę to pojąć, jest to, czy kompilator automatycznie dodał testy czasu wykonywania (
if (index < 0) throwOrWhatever
) lub w przypadku działań w czasie kompilacji generuje wiele potencjalnie fałszywie dodatnich ostrzeżeń / błędów. „Indeks dla tego dostępu do tablicy może być ujemny”. Mam wątpliwości, to byłoby pomocne.Ponadto osoby, które faktycznie piszą kontrole czasu wykonywania dla swoich indeksów tablic / kontenerów, wymagają więcej pracy związanej z liczbami całkowitymi ze znakiem. Zamiast pisać
if (index < container.size()) { ... }
masz teraz napisać:if (index >= 0 && index < container.size()) { ... }
. Dla mnie wygląda na pracę przymusową, a nie na poprawę ...Języki bez niepodpisanych typów są do niczego ...
Tak, to jest atak na Javę. Teraz pochodzę z wbudowanego programowania i dużo pracowaliśmy z magistralami polowymi, w których operacje binarne (i, lub, xor, ...) i nieco mądre składanie wartości to dosłownie chleb powszedni. W przypadku jednego z naszych produktów, my - a raczej klient - chcieliśmy portu java ... i siedziałem naprzeciwko bardzo kompetentnego na szczęście gościa, który zajmował się portem (odmówiłem ...). Próbował zachować spokój ... i cierpieć w ciszy ... ale ból był tam, nie mógł przestać przeklinać po kilku dniach ciągłego zajmowania się podpisanymi wartościami całkowitymi, które POWINNY być niepodpisane ... Nawet pisanie testów jednostkowych dla te scenariusze są bolesne i ja osobiście uważam, że java byłoby lepiej, gdyby pominęli liczby całkowite ze znakiem i zaoferowali tylko bez znaku ... przynajmniej wtedy nie musisz się przejmować rozszerzeniami znaków itp ...
To moje 5 centów w tej sprawie.
źródło