Kiedy używać wartości bez znaku zamiast podpisanych?


82

Kiedy warto używać zmiennej bez znaku zamiast zmiennej ze znakiem? A co z forpętlą?

Słyszę wiele opinii na ten temat i chciałem sprawdzić, czy jest coś na kształt konsensusu.

for (unsigned int i = 0; i < someThing.length(); i++) {  
    SomeThing var = someThing.at(i);  
    // You get the idea.  
}

Wiem, że Java nie ma wartości bez znaku, a to musiała być świadoma decyzja ze strony Sun Microsystems .


1
Znalazłem to pomocne: codemines.blogspot.ca/2007/10/…
mk12

Myślę, że to pytanie jest dość oparte na opiniach. Przedstawiony kod będzie działał w obu przypadkach dobrze, więc możesz używać obu. Z wyjątkiem powodów związanych z wydajnością (dotychczas żadna odpowiedź nie dotyczy wydajności) to tylko osobisty gust.
Trilarion

Odpowiedzi:


69

Cieszyłem się, że znalazłem dobrą rozmowę na ten temat, ponieważ wcześniej nie myślałem o tym zbyt wiele.

Podsumowując, znak ze znakiem jest dobrym wyborem ogólnym - nawet jeśli nie masz pewności, że wszystkie liczby są dodatnie - jeśli zamierzasz wykonać operacje arytmetyczne na zmiennej (jak w typowym przypadku pętli).

Jeśli zamierzasz robić rzeczy bitowe, takie jak maski, brak znaku zaczyna mieć więcej sensu. Lub, jeśli desperacko pragniesz uzyskać ten dodatkowy dodatni zakres, wykorzystując bit znaku.

Osobiście lubię podpisane, ponieważ nie ufam sobie, że będę konsekwentny i unikam mieszania tych dwóch typów (jak ostrzega artykuł).


3
W dalszej części tego wątku pokazano, że unsignedznacznie lepiej sprawdza się w wykrywaniu przepełnienia niezaufanych danych wejściowych. Niestety, proponowane „odpowiedzi” na zagadkę nie są aż tak świetne. Moja jest template<size_t limit> bool range_check_sum( unsigned a, unsigned b ) { return (a < limit) && (b < limit - a); } taka, że ​​jeśli ktoś ma podobnie prostą i prostą odpowiedź przy użyciu podpisanych typów, z przyjemnością to zobaczy.
Ben Voigt,

10

W powyższym przykładzie, gdy „i” zawsze będzie dodatnie, a wyższy zakres byłby korzystny, bez znaku byłby przydatny. Na przykład, jeśli używasz instrukcji „deklaruj”, takich jak:

#declare BIT1 (unsigned int 1)
#declare BIT32 (unsigned int reallybignumber)

Zwłaszcza, gdy te wartości nigdy się nie zmienią.

Jeśli jednak robisz program księgowy, w którym ludzie są nieodpowiedzialni za swoje pieniądze i ciągle są na minusie, z pewnością będziesz chciał użyć opcji „podpisane”.

Zgadzam się jednak z saintem, że dobrą praktyczną zasadą jest użycie znaku podpisanego, który tak naprawdę jest domyślny w C, więc jesteś objęty ochroną.


9

Myślę, że jeśli sprawa biznesu mówi, że liczba ujemna jest nieważny, byś chciał mieć błąd pokazany lub wyrzucane.

Mając to na uwadze, dopiero niedawno dowiedziałem się o liczbach całkowitych bez znaku, pracując nad projektem, przetwarzając dane w pliku binarnym i zapisując je w bazie danych. Celowo „uszkadzałem” dane binarne i otrzymałem wartości ujemne zamiast oczekiwanego błędu. Okazało się, że mimo przeliczenia wartości nie była ona prawidłowa w moim przypadku biznesowym.
Mój program nie popełnił błędu i skończyło się na tym, że dostałem błędne dane do bazy danych. Byłoby lepiej, gdybym użył uinti wystąpił błąd programu.


8

Kompilatory C i C ++ wygenerują ostrzeżenie podczas porównywania typów podpisanych i niepodpisanych; w przykładowym kodzie nie można było zrobić zmiennej pętli bez znaku i kazać kompilatorowi wygenerować kod bez ostrzeżeń (zakładając, że te ostrzeżenia były włączone).

Oczywiście kompilujesz z ostrzeżeniami ustawionymi do góry, prawda?

I czy rozważałeś kompilację z „traktuj ostrzeżenia jako błędy”, aby pójść o krok dalej?

Wadą używania liczb ze znakiem jest to, że istnieje pokusa, aby je przeładować, tak aby na przykład wartości 0-> n były wyborem menu, a -1 oznacza, że ​​nic nie zostało wybrane - zamiast tworzyć klasę, która ma dwie zmienne, jedną do wskaż, czy coś jest zaznaczone, a inne, aby zapisać, co to jest. Zanim się zorientujesz, wszędzie testujesz negatywny wynik, a kompilator narzeka, że ​​chcesz porównać wybór menu z liczbą opcji menu, które masz - ale jest to niebezpieczne, ponieważ są to różne typy . Więc nie rób tego.


6

size_tjest często dobrym wyborem do tego celu lub size_typejeśli używasz klasy STL.


Tylko wtedy, gdy masz do czynienia z rozmiarem czegoś w bajtach.
mk12

@ mk12 Standardowa biblioteka kontenery uwidaczniają size_typeelement członkowski dla liczby elementów, a nie tylko bajtów. Powinien być używany zamiast tego, std::size_tgdzie jest dostępny, ale niedokładne jest sugerowanie, że wszystko, co zaczyna się od, size_tmoże oznaczać tylko bajty.
underscore_d
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.