2010-03-01 12 views
11
int a = -534; 
unsigned int b = (unsigned int)a; 
printf("%d, %d", a, b); 

stampe -534, -534typecasting a non firmato in C

Perché la typecast non abbia luogo?

mi aspettavo che fosse -534, 534


Se modifico il codice per

int a = -534; 
unsigned int b = (unsigned int)a; 
if(a < b) 
    printf("%d, %d", a, b); 

la sua non stampare nulla ... dopo tutto a è inferiore a b ??

+2

Probabilmente vorrete controllare i vostri documenti del compilatore e vedere se c'è un'opzione per controllare gli specificatori del formato printf. gcc ha -Wformat, che ti avrebbe avvisato della% d rispetto a% u al momento della compilazione. –

+0

http://stackoverflow.com/questions/50605/signed-to-unsigned-conversion-in-c-is-it-always-safe –

+0

Questo non dovrebbe avere un tag C++. – Friedrich

risposta

4

In primo luogo, non è necessario il cast: il valore di a viene convertito in modo implicito unsigned int con il compito di b. Quindi, la sua dichiarazione è equivalente a:

unsigned int b = a; 

Ora, una proprietà importante di unsigned tipi interi in C e C++ è che i loro valori sono sempre nel range [0, max], dove max per unsigned int è UINT_MAX (è definito in limits.h). Se assegni un valore che non è compreso nell'intervallo, viene convertito in tale intervallo. Pertanto, se il valore è negativo, aggiungere ripetutamente UINT_MAX+1 per inserirlo nell'intervallo [0, UINT_MAX]. Per il tuo codice sopra, è come se scrivessimo: unsigned int b = (UINT_MAX + a) + 1. Questo non è uguale a -a (534).

Si noti che quanto sopra è vero se la rappresentazione sottostante è in complemento a due, complemento di uno, o grandezza del segno (o qualsiasi altra codifica esotica). Si vede che con qualcosa di simile:

signed char c = -1; 
unsigned int u = c; 
printf("%u\n", u); 
assert(u == UINT_MAX); 

Su macchina complemento di una tipica due con un 4 byte int, c è 0xff, e u è 0xffffffff. Il compilatore deve assicurarsi che quando il valore -1 viene assegnato a u, viene convertito in un valore uguale a UINT_MAX.

Ora tornando al codice, la stringa di formato printf è errata per b. Dovresti usare %u. Quando lo fai, troverai che stampa il valore di UINT_MAX - 534 + 1 invece di 534.

Quando utilizzato l'operatore di confronto <, poiché b è unsigned int, a è anche convertito unsigned int. Questo, dato con b = a; in precedenza, significa che a < b è false: a come unsigned int equivale a b.

Diciamo che avete macchina complemento a quelli, e lo fai:

signed char c = -1; 
unsigned char uc = c; 

Diciamo un char (con o senza segno) è di 8-bit su quella macchina. Poi c e uc memorizzerà i seguenti valori e bit-pattern:

+----+------+-----------+ 
| c | -1 | 11111110 | 
+----+------+-----------+ 
| uc | 255 | 11111111 | 
+----+------+-----------+ 

Nota che le sequenze di bit di c e uc non sono gli stessi. Il compilatore deve assicurarsi che c abbia il valore -1 e che uc abbia il valore UCHAR_MAX, che è 255 su questa macchina.

Ci sono ulteriori dettagli su my answer to a question here on SO.

15

Perché si utilizza %d per la stampa. Utilizzare %u per non firmato. Poiché printf è una funzione vararg, non può conoscere i tipi dei parametri e deve invece basarsi sugli specificatori di formato. Per questo motivo il cast del tipo che fai non ha alcun effetto.

+2

Infatti, il valore unsigned ottiene 'reinterpret_cast' -ed _back_ da' printf() ', a causa di'% d'. '% d' significa" prendi il valore e interpretalo come firmato " – Vlad

+0

Sì. Secondo lo standard C, i risultati del comportamento non definito (non in generale per la funzione variadica, ma per printf in particolare). –

+0

Perché il 'if' fallisce? Si prega di vedere le domande modificate. – Lazer

4

l'identificatore nella stampa sta chiedendo a printf di stampare un numero intero con segno, quindi i byte sottostanti vengono interpretati come un intero con segno.

È necessario specificare che si desidera un numero intero senza segno utilizzando %u.

modifica:a==b è vero per il confronto, che è un comportamento strano, ma è perfettamente valido. Non hai cambiato i bit sottostanti che hai solo chiesto al compilatore di trattare i bit sottostanti in un certo modo. Pertanto un confronto bit a bit risulta vero.

[speculation] Sospetto che il comportamento possa variare tra le implementazioni del compilatore, ad esempio una CPU fittizia potrebbe non utilizzare la stessa logica per entrambi i numeri con segno e senza segno, nel qual caso un confronto bit a bit non riuscirebbe. [/speculation]

+1

Non penso che tu abbia scritto quello che volevi scrivere :) –

+0

Sì, è corretto, in più sono dislessico hehe. –

+0

Perché il 'if' fallisce? Si prega di vedere la domanda modificata. – Lazer

0

Immagino che il primo caso del motivo per cui b viene stampato come -534 sia stato sufficientemente risposto da Tronic e Hassan. Non dovresti usare% d e dovresti usare% u.

Per quanto riguarda il secondo caso, di nuovo si verificherà un typecast implicito e entrambi aeb saranno uguali a causa dei quali il confronto produce il risultato previsto.

0

Per quanto posso vedere, se fallisce perché il compilatore presuppone che la seconda variabile debba essere considerata dello stesso tipo della prima. Prova se (b> a) per vedere la differenza.

+0

Questa risposta è semplicemente sbagliata: in un test che confronta un int firmato con un int unsigned, l'int firmato viene lanciato su un int unsigned. Entrambi (a < b) and (a > b) sono falsi perché (a == b). –

+0

Entrambi (a < b) and (a > b) sono falsi Ma nessuno dei due è se (b> a). Il mio compilatore è d'accordo. –

0

Re 2a domanda: il confronto non funziona mai tra due tipi diversi - sono sempre implicitamente espressi al "minimo comune denominatore", che in questo caso sarà unsigned int. Cattivo e contro-intuitivo, lo so.

+0

Se questo è il caso, il cast non prende -534 a zero, o causa un errore di run-time? Ho compilato il codice in VS2008 e 'a == b' è vero. –

+0

No: il cast di -534 per 'unsigned int' è consentito in entrambi i casi (caso # 1 durante l'inizializzazione di' b' e caso # 2 quando si confrontano 'a' e' b'). – MSalters

1

C può essere una brutta bestia a volte. Il problema è che -534 rappresenta sempre il valore 0xfffffdea se è memorizzato in una variabile con il tipo unsigned int o signed int. Per confrontare queste variabili devono essere dello stesso tipo in modo tale da essere automaticamente convertite in un int firmato o non firmato in modo che corrisponda all'altro. Una volta che sono dello stesso tipo, sono uguali in quanto rappresentano lo stesso valore.

Sembra probabile che il comportamento che si desidera è fornito dalla funzione ABS:

int a = -534; 
int b = abs(a); 
printf("%d, %d", a, b); 
+0

Si noti che il numero di f in '0xfffffdea' può variare. Per esempio. su macchine a 16 bit sarà solo '0xfdea' e su macchine a 64 bit avrai 13 f seguiti da' dea'. – MSalters

+0

Assolutamente giusto. Ho pensato che quando ho scritto la risposta avrei dovuto menzionare qualcosa sulla dimensione int, ma ho pensato che avrebbe potuto ingombrare la risposta con informazioni che non erano particolarmente rilevanti per il problema in questione. –

+2

Ancora più importante, anche la parte 'dea' non è necessariamente corretta, su una macchina del complemento di una grandezza segno o di un complemento. –

0

Casting un tipo integer dal sottoscritto a unsigned non modifica la sequenza di bit, si modifica semplicemente l'interpretazione del modello di bit .

Hai anche una mancata corrispondenza di formato,% u dovrebbe essere utilizzato per numeri interi senza segno, ma anche allora il risultato non sarà 534 come ci si aspetta, ma 4294966762.

Se si vuole fare un valore negativo positiva , semplicemente negarla:

unsigned b = (unsigned)-a ; 
printf("%d, %u", a, b); 

per quanto riguarda il secondo esempio, operazioni fra tipi con differenti firmato-ità coinvolgono regole di conversione implicita arcane - evitare. Dovresti impostare il livello di avviso del compilatore in alto per intrappolare molti di questi errori. Ad esempio, suggerisco/W4/WX in VC++ e -Wall -Werror -Wformat per GCC.