2009-03-24 14 views
7

Ho il seguente codice:MSVC++: stranezza con unsigned int e troppopieno

#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    string a = "a"; 
    for(unsigned int i=a.length()-1; i+1 >= 1; --i) 
    { 
     if(i >= a.length()) 
     { 
      cerr << (signed int)i << "?" << endl; 
      return 0; 
     } 
    } 
} 

Se posso compilare in MSVC con le ottimizzazioni pieno, l'uscita ottengo è "-1?". Se compilo in modalità Debug (nessuna ottimizzazione), non ottengo output (previsto)

Ho pensato che lo standard garantiva che gli interi non firmati fossero in overflow in modo prevedibile, così che quando i = (unsigned int) (- 1) , i + 1 = 0 e la condizione del ciclo i + 1> = 1 non riesce. Invece, il test sta passando in qualche modo. È un bug del compilatore o sto facendo qualcosa di indefinito da qualche parte?

risposta

8

Mi ricordo di aver avuto questo problema nel 2001. Sono stupito che sia ancora lì. Sì, questo è un bug del compilatore.

L'ottimizzatore è vedere

i + 1 >= 1; 

Teoricamente, possiamo ottimizzare questo mettendo tutte le costanti sullo stesso lato:

i >= (1-1); 

Perché i non è firmato, sarà sempre maggiore o uguale a zero.

Vedere questa discussione di newsgroup here.

1

Non ne sono sicuro, ma penso che probabilmente stai correndo da un insetto.

Sospetto che il problema riguardi il modo in cui il compilatore tratta il controllo for. Ho potuto immaginare l'ottimizzatore fare:

for(unsigned int i=a.length()-1; i+1 >= 1; --i) // As written 

for (unsigned int i = a.length()-1; i >= 0; --i) // Noting 1 appears twice 

for (unsigned int i = a.length()-1; ; --i) // Because i >= 0 at all times 

Se questo è ciò che sta accadendo è un altro discorso, ma potrebbe essere sufficiente per confondere l'ottimizzatore.

Si sarebbe probabilmente meglio utilizzare una formulazione ciclo più standard:

for (unsigned i = a.length()-1; i-- > 0;) 
4

ISO14882: 2003, sezione 5, paragrafo 5:

Se durante la valutazione di un'espressione, il risultato non è matematicamente definito o non nell'intervallo di valori rappresentabili per il suo tipo, il comportamento non è definito, a meno che tale espressione non sia un'espressione costante (5.19), nel qual caso il programma è mal formato.

(Enfasi mia.) Quindi, sì, il comportamento non è definito. Lo standard non fornisce garanzie di comportamento in caso di over/underflow intero.

Modifica: Lo standard sembra leggermente in conflitto sull'argomento altrove.

Sezione 3.9.1.4 dice:

interi senza segno, dichiarati non firmato, deve obbedire alle leggi dell'aritmetica modulo 2 n dove n è il numero di bit nella rappresentazione del valore di quel particolare formato di numero intero.

Ma sezione 4.7.2 e .3 dice:

2) Se il tipo di destinazione è senza segno, il valore risultante è il minimo intero senza segno congruente al numero intero sorgente (modulo 2 n dove n è il numero di bit usati per rappresentare il tipo senza segno). [Nota: nella rappresentazione a complemento a due, questa conversione è concettuale e non vi è alcun cambiamento nel modello di bit (se non vi è alcun troncamento). ]

3) Se il tipo di destinazione è firmato, il valore è invariato se può essere rappresentato nel tipo di destinazione (e larghezza del campo di bit); in caso contrario, il valore è definito dall'implementazione.

(l'enfasi è mia.)

+0

Hmm. Altri (su siti diversi) citano la sezione 4.7 dello standard: http://dev.feuvan.net/docs/isocpp/conv.html Lo stanno usando per sostenere che è definito. –

+0

Dalla 3.9.1 4, e la sua nota in calce, ho avuto l'impressione che gli interi senza segno siano un'eccezione: poiché 1 è aggiunto nell'aritmetica della mod 2^n, il risultato non può essere al di fuori dell'intervallo di valori, giusto? –

+0

In conversione in un tipo firmato (come nel caso qui), il risultato è definito dall'implementazione. Poiché il valore non rientra nell'intervallo * di entrambi i tipi in un'operazione firmata, prendo questo per rientrare nella sezione 5. Il compilatore è sbagliato qui, in entrambi i casi. – greyfade

0

Yup, ho appena testato questo su Visual Studio 2005, che sicuramente comporta in modo diverso in Debug e Release. Mi chiedo se il 2008 lo risolva.

È interessante notare che il cast implicito da size_t (.length's result) a unsigned int, ma non ha problemi a generare codice errato.