2012-09-18 8 views
5

Recentemente ho trovato un bug sul mio codice che mi ha richiesto alcune ore per eseguire il debug.Perché alcune conversioni di tipo implicito sono sicure su una macchina e non su un'altra? Come posso prevenire questo problema multipiattaforma?

il problema è stato in una funzione definita come:

unsigned int foo(unsigned int i){ 
    long int v[]={i-1,i,i+1} ; 
     . 
     . 
     . 
return x ; // evaluated by the function but not essential how for this problem. 
} 

La definizione di v non ha causato alcun problema sulla mia macchina di sviluppo (Ubuntu 12.04 a 32 bit, compilatore g ++), dove il unsigned int erano implicitamente convertito in long int e in quanto tale i valori negativi sono stati gestiti correttamente.

Su una macchina diversa (Ubuntu 12.04 64 bit, compilatore g ++) tuttavia questa operazione non era sicura. Quando i = 0, v [0] non era impostato su -1, ma con qualche strano valore grande (come spesso accade quando si prova a fare un negativo int unsigned).

ho potuto risolvere il problema gettando il valore di i-lungo int

long int v[]={(long int) i - 1, (long int) i, (long int) i + 1}; 

e tutto ha funzionato bene (su entrambe le macchine).

Non riesco a capire perché il primo funziona bene su una macchina e non funziona sull'altro.

Potete aiutarmi a capire questo, in modo da poter evitare questo o altri problemi in futuro?

risposta

6

Per i valori unsigned, l'addizione/sottrazione è ben definita come modulo aritmetico, quindi 0U-1 funzionerà su qualcosa come std::numeric_limits<unsigned>::max().

Durante la conversione da non firmata a firmata, se il tipo di destinazione è sufficientemente grande da contenere tutti i valori del valore senza segno, viene semplicemente copiata direttamente nel tipo di destinazione. Se il tipo di destinazione non è abbastanza grande da contenere tutti i valori non firmati, credo che sia definito dall'implementazione (cercherà di trovare un riferimento standard).

Quindi, quando long è a 64 bit (presumibilmente il caso sulla macchina a 64 bit), l'incastro si adatta e viene copiato direttamente.

Quando long è a 32 bit sulla macchina a 32 bit, molto probabilmente interpreta la sequenza di bit come un valore con segno che è -1 in questo caso.

MODIFICA: il modo più semplice per evitare questi problemi è evitare di mescolare i tipi con segno e senza segno. Cosa significa sottrarre uno da un valore il cui concetto non consente numeri negativi? Ho intenzione di sostenere che il parametro funzione dovrebbe essere un valore con segno nel tuo esempio.

Detto questo g ++ (almeno versione 4.5) fornisce un utile -Wsign-conversion che rileva questo problema nel codice specifico.

+0

grazie per la spiegazione, anche se non ho potuto capire molto bene la tua risposta.Non ne sono del tutto sicuro, ma ricordo che int lungo è abbastanza grande da contenere int unsigned (almeno secondo alcuni riferimenti C++ che si possono trovare online). Ad ogni modo sono d'accordo che è una buona pratica non mescolarli, ma in questo caso è necessario che lo faccia. Ero solo curioso di sapere quando possono sorgere questi problemi dipendenti dalla macchina. – lucacerone

0

È anche possibile avere specializzata getto cattura flusso over-all calchi:

template<typename O, typename I> 
O architecture_cast(I x) { 
/* make sure I is an unsigned type. It */ 
static_assert(std::is_unsigned<I>::value, "Input value to architecture_cast has to be unsigned"); 

assert(x <= static_cast<typename std::make_unsigned<O>::type>(std::numeric_limits<O>::max())); 

return static_cast<O>(x); 
} 

Utilizzando questa cattura in di debug tutti i calchi da numeri più grandi rispetto al tipo risultante può ospitare. Ciò include il tuo caso di int unsigned pari a 0 e sottratto da -1, che risulta come il più grande int unsigned.

Problemi correlati