Uso ints non firmati per rendere più chiaro il mio codice e le sue intenzioni. Una cosa che faccio per evitare conversioni implicite impreviste quando faccio l'aritmetica con tipi sia firmati che non firmati è usare un short senza segno (2 byte di solito) per le mie variabili senza segno. Questo è efficace per un paio di motivi:
- Quando si esegue l'aritmetica con variabili e letterali brevi senza segno (che sono di tipo int) o variabili di tipo int, ciò garantisce che la variabile senza segno venga sempre promossa a un int prima di valutare l'espressione, poiché int ha sempre un rango superiore rispetto a breve . Questo evita qualsiasi comportamento imprevisto che fa l'aritmetica con tipi firmati e non firmati, supponendo che il risultato dell'espressione rientri naturalmente in un int firmato.
- La maggior parte delle volte, le variabili senza segno che stai utilizzando non supereranno il valore massimo di un corto di 2 byte senza segno (65.535)
Il principio generale è che il tipo delle variabili non firmate dovrebbe avere un rango inferiore rispetto al tipo delle variabili firmate al fine di garantire la promozione al tipo firmato. Quindi non avrai alcun comportamento di overflow imprevisto. Ovviamente non puoi assicurarlo tutto il tempo, ma (la maggior parte) spesso è possibile assicurarlo.
Ad esempio, recentemente ne ho avuti alcuni per loop qualcosa del genere:
const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
if((i-2)%cuint == 0)
{
//Do something
}
}
Il letterale "2" è di tipo int. Se fossi un int senza segno anziché un abbreviato senza segno, nella sottoespressione (i-2), 2 verrebbe promosso a un int senza segno (poiché int senza segno ha una priorità più alta di int con segno). Se i = 0, la sottoespressione è uguale a (0u-2u) = un valore enorme dovuto all'overflow. Stessa idea con i = 1. Tuttavia, poiché i è un abbreviato senza segno, viene promosso allo stesso tipo di "2" letterale, che è firmato int e tutto funziona bene.
Per una maggiore sicurezza: nel raro caso in cui l'architettura su cui si sta implementando causa int come 2 byte, ciò potrebbe far sì che entrambi gli operandi nell'espressione aritmetica vengano promossi in int senza segno nel caso in cui la variabile breve senza segno non si adatti nell'int a 2 byte con segno, quest'ultimo dei quali ha un valore massimo di 32.767 <65.535. (Vedi https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned per maggiori dettagli). Per evitare ciò, puoi semplicemente aggiungere un static_assert al tuo programma come segue:
static_assert(sizeof(int) == 4, "int must be 4 bytes");
e non verrà compilato su architetture in cui int è 2 byte.
for(unsigned int n = 10; n >= 0; n --)
(continua all'infinito)