2014-09-29 14 views
7

Sono sorpreso dal comportamento di C++ quando si applica bit-saggio non a un carattere senza segno.C++ - Bit-wise non di uchar produce int

Prendere il valore binario 01010101b, che è 0x55 o 85. L'applicazione del bit-wise su una rappresentazione a otto bit dovrebbe produrre 10101010b, ovvero 0xAA o 170.

Tuttavia, non riesco a riprodurre quanto sopra in C++. La seguente semplice asserzione fallisce.

assert(static_cast<unsigned char>(0xAAu) == ~static_cast<unsigned char>(0x55u)); 

ho stampato i valori di 0x55, 0xAA, e ~0x55 (come uchar) con il seguente codice. E rivela che il bit-saggio non fa ciò che mi aspetto che faccia.

std::cout << "--> 0x55: " << 0x55u << ", 0xAA: " << 0xAAu << ", ~0x55: " 
    << static_cast<unsigned>(~static_cast<unsigned char>(0x55u)) << std::endl; 

--> 0x55: 85, 0xAA: 170, ~0x55: 4294967210 

Il numero stampato per ~0x55 è uguale a 11111111111111111111111110101010b, che è a 32 bit a bit non di 0x55. Pertanto, l'operatore ~ sta funzionando su numeri interi a 32 bit anche se immetto espressamente l'input su un unsigned char. Perché?

Ho applicato un altro test per verificare quale tipo restituisce l'operatore ~. E si scopre essere int su un unsigned char ingresso:

template <class T> 
struct Print; 

// inside main()  
Print<decltype(~static_cast<unsigned char>(0x55))> dummy; 

restituisce il seguente errore di compilatore, che indica che il risultato è di tipo int.

error: implicit instantiation of undefined template 'Print<int>' 
    Print<decltype(~static_cast<unsigned char>(0x55u))> dummy; 

Cosa sto facendo male? Oppure, come posso ottenere C++ per produrre 0xAA da ~0x55?

codice completa è here

risposta

9

promozioni integrali vengono eseguite sul operando di ~ possiamo vedere questo andando alla sezione draft C++ standard5.3.1operatori unari che dice (sottolineatura mia):

L'operando di ~ deve avere il tipo di enumerazione intero o senza ambito; il risultato è il complemento del suo operando. Le promozioni integrali vengono eseguite. Il tipo del risultato è il tipo dell'operando promosso [...]

e promozioni integrali sono coperti in sezione 4.5promozioni integrali e dire:

Una prvalue di un tipo intero diverso da bool, char16_t, char32_t o wchar_t il cui rank di conversione intero (4.13) è inferiore al valore di int può essere convertito in un valore di tipo int se int può rappresentare tutti i valori del tipo di origine;

Per completezza, per vedere che unsigned char rango è inferiore al rango di int possiamo andare alla sezione 4.13Integer conversione rango che dice:

Il rango di un il tipo intero con segno deve essere maggiore del valore di qualsiasi tipo di intero con segno con una dimensione più piccola.

e:

Il rango di carattere deve essere uguale al rango di char firmato e unsigned char.

Una soluzione potrebbe essere quella di assegnare il risultato a un unsigned char che, questo è sicuro dal momento che non devi preoccuparti di integer overflow firmato.

Come sottolinea Ben Voigt, sarebbe conforme ad avere un sistema dove sizeof (int) == 1 e CHAR_BIT >= 32. In tal caso il rango di char unsigned non sarà inferiore a int e quindi la promozione sarebbe a int unsigned. Non conosciamo alcun sistema in cui ciò si verifichi effettivamente.

+0

+1: Anche se è probabile che ovvio, vale la pena ricordare che 'char' senza segno è in effetti (a) di conversione più bassa rank di 'int', e (b) full rappresentabile come' int' (tutti i valori di 'char unsigned 'possono essere rappresentati come' int'). Quindi la promozione è valida. – WhozCraig

+0

@WhozCraig è una richiesta giusta, fatta. –

+0

Grazie per la risposta dettagliata! – Lemming

1

È possibile sorta di "troncare" i leader del 1 assegnando il risultato di ~0x55 a un unsigned char:

#include <iostream> 

int main() 
{ 
    unsigned char input = 0x55; 
    unsigned char output = ~input; 

    std::cout << "input: " << (int)input << " output: " << (int)output << std::endl; 

    return 0; 
} 

Risultato:

input: 85 output: 170 
+2

Non è necessario eseguire l'operazione '|', assegnando semplicemente una variabile 'unsigned char' dovrebbe farlo:' output = ~ input'. –

+0

@MarkRansom Così vero! Grazie. – TobiMcNamobi

2

La risposta su promozione integrale è corretta.

è possibile ottenere i risultati desiderati per colata e Notting nel giusto ordine:

assert(static_cast<unsigned char>(0xAAu) == static_cast<unsigned char>(~0x55u));