Quali sono le migliori pratiche per quanto riguarda gli unsigned?

39

Uso ovunque intro unsigned e non sono sicuro se dovrei. Questo può essere dalle colonne ID della chiave primaria del database ai contatori, ecc. Se un numero non dovrebbe mai essere negativo, allora userò sempre un int unsigned.

Tuttavia noto dal codice di altri che nessun altro sembra farlo. C'è qualcosa di cruciale che sto trascurando?

Modifica: Da questa domanda ho anche notato che in C, restituire valori negativi per errori è comune piuttosto che generare eccezioni come in C ++.

    
posta wting 01.08.2011 - 12:17
fonte

11 risposte

27

Is there something crucial that I'm overlooking?

Quando i calcoli coinvolgono sia i tipi con segno sia quelli senza segno, nonché le diverse dimensioni, le regole per la promozione del tipo possono essere complesse e portare a comportamento imprevisto .

Credo che questo sia il motivo principale per cui Java ha omesso i tipi int non firmati.

    
risposta data 01.08.2011 - 13:02
fonte
17

Penso che Michael ha un punto valido, ma IMO il motivo per cui tutti usano int per tutto il tempo (specialmente in for (int i = 0; i < max, i++ ) è che l'abbiamo imparato in quel modo. Quando ogni singolo esempio di un libro " come imparare la programmazione " utilizza int in un ciclo for , pochissimi metteranno in dubbio questa pratica.

L'altro motivo è che int è il 25% più corto di uint , e siamo tutti pigri ...; -)

    
risposta data 01.08.2011 - 13:50
fonte
10

Le informazioni sull'intervallo di codifica in tipi sono A Good Thing. Impone l'uso di numeri ragionevoli al momento della compilazione.

Molte architetture sembrano avere istruzioni specializzate per gestire int - > % conversioni% co_de. La conversione da float può essere più lenta (un pochino) .

    
risposta data 01.08.2011 - 12:44
fonte
8

Combinare tipi firmati e non firmati può farti entrare in un mondo di dolore. E non puoi usare tutti i tipi non firmati perché troverai cose che hanno un intervallo valido che include numeri negativi o che hanno bisogno di un valore per indicare un errore e -1 è più naturale. Quindi il risultato netto è che molti programmatori usano tutti i tipi di interi con segno.

    
risposta data 13.08.2011 - 16:20
fonte
6

Per me i tipi sono molto legati alla comunicazione. Usando esplicitamente un int unsigned mi dici che i valori firmati non sono valori validi. Questo mi consente di aggiungere alcune informazioni durante la lettura del codice in aggiunta al nome della variabile. Idealmente, un tipo non anonimo mi direbbe di più, ma mi dà più informazioni che se avessi usato gli Ints ovunque.

Purtroppo non tutti sono molto consapevoli di ciò che comunica il loro codice, e questo è probabilmente il motivo per cui si vedono int i ovunque, anche se i valori sono almeno non firmati.

    
risposta data 01.08.2011 - 14:03
fonte
5

Uso unsigned int in C ++ per gli indici di array, soprattutto, e per qualsiasi contatore che inizia da 0. Penso che sia buono dire esplicitamente "questa variabile non può essere negativa".

    
risposta data 01.08.2011 - 12:52
fonte
3

Dovresti preoccuparti di questo quando hai a che fare con un numero intero che potrebbe effettivamente avvicinarsi o superare i limiti di un int firmato. Dal momento che il massimo positivo di un intero a 32 bit è 2.147.483.647, allora dovresti usare un int unsigned se sai che a) non sarà mai negativo e b) potrebbe raggiungere 2.147.483.648. Nella maggior parte dei casi, incluse chiavi e contatori del database, non mi avvicinerò mai nemmeno a questi tipi di numeri, quindi non mi preoccupo di preoccuparmi se il bit del segno viene utilizzato per un valore numerico o per indicare il segno.

Direi: usa int a meno che tu non sappia che hai bisogno di un int unsigned.

    
risposta data 01.08.2011 - 12:25
fonte
3

È un compromesso tra semplicità e affidabilità. Più bug si possono catturare in fase di compilazione, più affidabile è il software. Diverse persone e organizzazioni si trovano su diversi punti lungo questo spettro.

Se si esegue una programmazione ad alta affidabilità in Ada, si utilizzano anche tipi diversi per variabili come la distanza in piedi rispetto alla distanza in metri e il compilatore lo contrassegna se si accidentalmente assegna l'uno all'altro. Questo è perfetto per programmare un missile guidato, ma eccessivo (gioco di parole) se stai convalidando un modulo web. Non c'è necessariamente qualcosa di sbagliato in entrambi i modi finché soddisfa i requisiti.

    
risposta data 01.08.2011 - 19:05
fonte
2

Sono propenso a concordare con il ragionamento di Joel Etherton, ma giungo alla conclusione opposta. Per come la vedo io, anche se sai che è improbabile che i numeri si avvicinino mai ai limiti di un tipo firmato, se conosci i numeri negativi non succederanno, allora c'è ben poco motivo per usare la variante firmata di un tipo.

Per lo stesso motivo per cui in alcune istanze selezionate ho utilizzato BIGINT (numero intero a 64 bit) anziché INTEGER (numero intero a 32 bit) nelle tabelle di SQL Server. La probabilità che i dati raggiungano il limite di 32 bit entro un ragionevole lasso di tempo è minima, ma se dovesse accadere, le conseguenze in alcune situazioni potrebbero essere piuttosto devastanti. Assicurati di mappare correttamente i tipi tra le lingue o finirai con stranezze interessanti davvero lontane ...

Detto questo, per alcune cose, come i valori delle chiavi primarie del database, firmati o non firmati non importa, perché a meno che non si stia manualmente riparando i dati danneggiati o qualcosa del genere, non si ha mai a che fare direttamente con il valore ; è un identificatore, niente di più. In questi casi, la coerenza è probabilmente più importante della scelta esatta della firma. Altrimenti, ti ritroverai con alcune colonne di chiavi estranee che sono state firmate e altre che sono senza segno, senza alcun motivo apparente - o che di nuovo stranezze interessanti.

    
risposta data 01.08.2011 - 16:35
fonte
1

Suggerirei che al di fuori dei contesti di archiviazione dei dati e di interscambio di dati con limiti di spazio, in genere si dovrebbero usare i tipi firmati. Nella maggior parte dei casi in cui un intero con segno a 32 bit sarebbe troppo piccolo ma un valore senza segno a 32 bit sarebbe sufficiente per oggi, non passerà molto tempo prima che il valore senza segno a 32 bit non sia abbastanza grande.

Le volte primarie che si dovrebbero usare i tipi senza segno sono quando si uniscono più valori in uno più grande (ad esempio, la conversione di quattro byte in un numero a 32 bit) o la decomposizione di valori più grandi in più piccoli (ad esempio, la memorizzazione di un numero a 32 bit come quattro byte), o quando si ha una quantità che si prevede di "ribaltare" periodicamente e si ha bisogno di affrontarla (si pensi a un contatore di utenza residenziale, la maggior parte di loro ha cifre sufficienti per assicurarsi che non possano rotolare tra una lettura e l'altra se vengono lette tre volte l'anno, ma non abbastanza da garantire che non si ribaltino nella vita utile del contatore). I tipi non firmati spesso hanno abbastanza 'stranezze' che dovrebbero essere usati solo nei casi in cui la loro semantica è necessaria.

    
risposta data 13.07.2012 - 23:42
fonte
0

Uso gli input non firmati per rendere più chiaro il mio codice e le sue intenzioni. Una cosa che faccio per evitare conversioni implicite impreviste quando si esegue l'aritmetica con entrambi i tipi con segno e senza segno è di usare un corto senza segno (di solito 2 byte) per le variabili non firmate. Questo è efficace per un paio di motivi:

  • Quando esegui operazioni aritmetiche con le tue brevi variabili e letterali senza segno (che sono di tipo int) o variabili di tipo int, questo assicura che la variabile unsigned sarà sempre promossa a un int prima di valutare l'espressione, poiché int ha sempre un valore più alto rango che corto. Ciò evita qualsiasi comportamento imprevisto eseguendo operazioni aritmetiche con tipi firmati e non firmati, presupponendo che il risultato dell'espressione si inserisca in un int firmato ovviamente.
  • La maggior parte delle volte, le variabili non firmate che stai utilizzando non superano il valore massimo di un corto a 2 byte senza segno (65.535)

Il principio generale è che il tipo delle variabili non firmate deve avere un grado inferiore rispetto al tipo delle variabili firmate per assicurare la promozione al tipo firmato. Quindi non avrai alcun comportamento di overflow imprevisto. Ovviamente non è possibile garantire questo tutto il tempo, ma (la maggior parte) spesso è possibile garantirlo.

Ad esempio, recentemente ho avuto qualche ciclo per 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 ero un int unsigned invece di un unsigned short, quindi nella sottoespressione (i-2), 2 sarebbe promosso a un int unsigned (poiché int unsigned ha una priorità più alta di signed int). Se i = 0, allora la sottoespressione è uguale a (0u-2u) = un valore enorme a causa di un overflow. Stessa idea con i = 1. Tuttavia, dato che i è un corto senza segno, viene promosso allo stesso tipo di letterale '2', che è firmato int, e tutto funziona bene.

Per una maggiore sicurezza: nel raro caso in cui l'architettura che stai implementando fa sì che int sia 2 byte, questo potrebbe far sì che entrambi gli operandi nell'espressione aritmetica siano promossi a int unsigned nel caso in cui la variabile breve unsigned non si inseriscono nell'int intatto da 2 byte, l'ultimo dei quali ha un valore massimo di 32.767 < 65.535. (Vedi link per maggiori dettagli). Per evitare questo, puoi semplicemente aggiungere una static_assert al tuo programma come segue:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

e non verrà compilato su architetture dove int è 2 byte.

    
risposta data 12.12.2018 - 14:34
fonte

Leggi altre domande sui tag