Używam niepodpisanych ints, aby mój kod i jego intencje były bardziej przejrzyste. Jedną rzeczą, którą robię, aby uchronić się przed nieoczekiwanymi niejawnymi konwersjami podczas wykonywania arytmetyki zarówno z typami podpisanymi, jak i niepodpisanymi, jest użycie niepodpisanego skrótu (zwykle 2 bajty) dla moich niepodpisanych zmiennych. Jest to skuteczne z kilku powodów:
- Kiedy robisz arytmetykę ze swoimi niepodpisanymi krótkimi zmiennymi i literałami (które są typu int) lub zmiennymi typu int, zapewnia to, że niepodpisana zmienna zawsze będzie promowana do int przed oceną wyrażenia, ponieważ int zawsze ma wyższą rangę niż krótka . Pozwala to uniknąć nieoczekiwanego zachowania arytmetycznego z typami podpisanymi i niepodpisanymi, zakładając, że wynik wyrażenia pasuje oczywiście do podpisanego int.
- W większości przypadków używane zmienne niepodpisane nie przekroczą maksymalnej wartości 2-bajtowego skrótu bez znaku (65 535)
Ogólna zasada jest taka, że typ zmiennych bez znaku powinien mieć niższą rangę niż typ zmiennych ze znakiem, aby zapewnić awans do typu ze znakiem. Wtedy nie będziesz mieć żadnych nieoczekiwanych zachowań związanych z przepełnieniem. Oczywiście nie możesz tego zapewnić przez cały czas, ale (najczęściej) jest to możliwe.
Na przykład ostatnio miałem dla pętli coś takiego:
const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
if((i-2)%cuint == 0)
{
//Do something
}
}
Dosłowne „2” jest typu int. Gdybym był liczbą całkowitą bez znaku zamiast skrótu bez znaku, wówczas w podwyrażeniu (i-2) 2 byłoby promowane do postaci bez znaku (ponieważ int ma wyższy priorytet niż znak int). Jeśli i = 0, to podwyrażenie jest równe (0u-2u) = pewna ogromna wartość z powodu przepełnienia. Ten sam pomysł z i = 1. Jednakże, ponieważ i jest skrótem bez znaku, jest promowany do tego samego typu, co literał „2”, który jest podpisany int i wszystko działa dobrze.
Dla większego bezpieczeństwa: w rzadkim przypadku, gdy implementowana architektura powoduje, że int ma 2 bajty, może to spowodować, że oba operandy w wyrażeniu arytmetycznym zostaną awansowane na int bez znaku w przypadku, gdy krótka zmienna bez znaku nie pasuje w podpisany 2-bajtowy int, którego ostatni ma maksymalną wartość 32 767 <65 535. (Zobacz https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned, aby uzyskać więcej informacji). Aby temu zapobiec, możesz po prostu dodać static_assert do swojego programu w następujący sposób:
static_assert(sizeof(int) == 4, "int must be 4 bytes");
i nie będzie się kompilował na architekturach, gdzie int ma 2 bajty.
for(unsigned int n = 10; n >= 0; n --)
(pętle nieskończenie)