2010-06-23 14 views
17

Si consideri il seguente codice:Perché -1> sizeof (int)?

template<bool> class StaticAssert; 
template<> class StaticAssert<true> {}; 
StaticAssert< (-1 < sizeof(int)) > xyz1; // Compile error 
StaticAssert< (-1 > sizeof(int)) > xyz2; // OK 

Perché -1 > sizeof(int) vero?

  1. E 'vero che -1 viene promosso a unsigned(-1) e poi unsigned(-1) > sizeof(int).
  2. È vero che -1 > sizeof(int) equivale a -1 > size_t(4) se sizeof (int) è 4. Se questo è il motivo per cui -1 > size_t(4) è falso?

Questo comformante standard C++?

risposta

14

Il seguente è il modo standard (ISO 14882) spiega abort -1> sizeof (int)

relazionale operatore `>' è definito in 5.9 (expr.rel/2)

I normali conversioni aritmetiche vengono eseguite su operandi dell'aritmetica o tipo enumerazione. ...

I normali conversioni aritmetiche è definita 5 (espressione/9)

... Il modello si chiama le conversioni aritmetiche abituali, che sono definite come segue:

  • Se uno degli operandi è di tipo lungo doppia, ...
  • Altrimenti, se uno degli operandi è dobule, ...
  • Altrimenti, se uno degli operandi è float, ...
  • In caso contrario, le promozioni integrali devono essere eseguite su entrambi gli operandi.
  • ...

Le promozioni integrali è definito in 4.5 (conv.prom/1)

Un rvalue di tipo char, char firmato, char, short int o unsigned breve int può essere convertito in un rvalue di tipo int se int può rappresentare tutti i valori di il tipo di sorgente ; in caso contrario, il valore di origine può essere convertito in in un valore di tipo int unsigned.

Il risultato di sizeof è definito in 5.3.3 (expr.sizeof/6)

Il risultato è una costante di tipo size_t

size_t è definito nella norma C (ISO 9899), che è il tipo intero senza segno .

Quindi per -1 > sizeof(int), il> attiva le normali conversioni aritmetiche. La normale conversione aritmetica converte -1 in unsigned int perché int non può rappresentare tutto il valore di size_t. -1 diventa un numero molto grande dipende dalla piattaforma. Quindi -1 > sizeof(int) è true.

+2

Potrebbe essere solo un errore di battitura, ma 'size_t' è un tipo intero non firmato _an_ e non deve essere il caso che' int' non possa rappresentare tutti i valori di 'size_t' (' size_t' potrebbe essere 'unsigned short '), anche se ovviamente non è possibile sulla piattaforma del richiedente. –

+2

'(unsigned T) -1' non è solo un grande valore, è * il * valore più grande' unsigned T' può contenere. – GManNickG

+1

Sono ben consapevole di ciò che lo standard consente. :) -1 è sempre il più grande, leggi le regole di conversione. Oppure questo http://stackoverflow.com/questions/809227/is-it-safe-to-use-1-to-set-all-bits-to-true/809341#809341 – GManNickG

14

Poiché senza segno è più forte poi firmata e -1 convertito in valore senza segno al size_t, quindi in realtà -1 == 0xFFFFFFFF > 4

Questo è come dovrebbe funzionare secondo standard C++

+0

i compilatori non emettono avvisi per tali casi? – kriss

+0

questo non spiega perché -1 <(size_t) 4, sizeof dovrebbe usare il tipo di ritorno size_t .. –

+0

@kriss - Diversi compilatori emettono diversi avvisi. Anche gli avvertimenti possono essere soppressi tramite le opzioni della riga di comando del compilatore e/o dalle direttive nel codice sorgente; e/o quindi può essere ignorato dal programmatore. – ChrisW

4

perché -1 viene fusa a size_t e questo è un tipo di dati senza segno - quindi (size_t)-1 == 4294967295 (su un sistema a 32 bit) che è sicuramente maggiore di 4

se si aggiunge -Wall alle impostazioni di gcc ad esempio si ottiene un avviso che si è confrontando una firmata e una di dati senza segno tipo

+0

È davvero sicuro assumere che sizeof (size_t)> = sizeof (int) - IOW: è standardizzato? – Wolf

2

È semplice e triste. In C/C++:

  1. maggior parte del tempo, i tipi interi senza segno hanno la semantica di interi modulari (rappresentano classi di equivalenza)
  2. confronti dei tipi interi senza segno hanno la semantica di solito integer ordinamento, in modo che 1U < 2U (IOW 0U è il più piccolo valore unsigned)
  3. sizeof ha tipo size_t
  4. size_t è di tipo intero senza segno
  5. Point (1) implica che m I calcoli aritmetici ixed che coinvolgono un intero con segno e un intero senza segno vengono eseguiti in aritmetica modulare senza segno: questa è l'unica possibilità senza violare la regola "modulare modesto senza segno". È banale convertire un intero nella classe di equivalenza di interi equivalenti ad esso. (Considerando in senso contrario richiede la scelta di un numero intero per rappresentare la classe di equivalenza.)
  6. Point (5) implica che -1 < 1U viene interpretato come unsigned(-1) < 1U, e unsigned(-1) = - 1U, e ovviamente - 1U < 1U, così -1 < 1U è vero.
  7. I punti (1,3,4) implicano che sizeof something agisce (principalmente) come classe equivalente (!!!).
  8. Tutto questo implica che -1 < sizeof something

La conclusione: si tratta di un errore di progettazione ereditato da C.

Regola:

usate solo senza segno di aritmetica modulare, i bit manipolazioni (&, |, ^, <<, >>, ~ operatori), manipolazioni byte (unsigned char mezzi "byte" in C/C++) e caratteri (unsigned char significa carattere in C/C++).

Non utilizzare tipi non firmati per eseguire operazioni aritmetiche.

Se una funzione prevede un valore intero che non dovrebbe mai essere negativo, prendere un numero intero con segno e facoltativamente verificare nella funzione che il valore sia nell'intervallo.

+0

Ho trovato il punto (6) un po 'confuso, forse '==' incluso in * 'unsigned (-1)' = '- 1U' * sarebbe meglio – Wolf