Sto cercando di leggere una riga di caratteri, quindi stampare l’equivalente esadecimale dei personaggi.
Ad esempio, se ho una stringa che è "0xc0 0xc0 abc123"
, dove i primi 2 caratteri sono c0
in esadecimale e i caratteri rimanenti sono abc123
in ASCII, allora dovrei ottenere
c0 c0 61 62 63 31 32 33
Tuttavia, mi dà printf
usando %x
ffffffc0 ffffffc0 61 62 63 31 32 33
Come posso ottenere l’output desiderato senza "ffffff"
? E perché solo c0 (e 80) ha il ffffff
, ma non gli altri caratteri?
Stai vedendo il ffffff
perché char
è firmato sul tuo sistema. In C, le funzioni vararg come printf
promuoveranno tutti gli interi minori di int
a int
. Poiché char
è un numero intero (numero int
con segno a 8 bit nel tuo caso), i tuoi caratteri vengono promossi a int
tramite l’estensione del segno.
Poiché c0
e 80
hanno un primo bit a 1 bit (e sono negativi come un numero intero a 8 bit), vengono estesi dal segno mentre gli altri nel campione non lo fanno.
char int c0 -> ffffffc0 80 -> ffffff80 61 -> 00000061
Ecco una soluzione:
char ch = 0xC0; printf("%x", ch & 0xff);
Questo maschererà i bit superiori e manterrà solo gli 8 bit più bassi che si desidera.
In effetti, c’è la conversione di tipo in int. Inoltre puoi forzare il tipo al carattere usando l’identificatore% hhx.
printf("%hhX", a);
Puoi creare un char senza segno:
unsigned char c = 0xc5;
La stampa darà C5
e non ffffffc5
.
Solo i caratteri maggiori di 127 vengono stampati con ffffff
perché sono negativi (il carattere è firmato).
Oppure puoi lanciare il char
durante la stampa:
char c = 0xc5; printf("%x", (unsigned char)c);
Probabilmente stai memorizzando il valore 0xc0 in una variabile char
, che probabilmente è un tipo firmato e il tuo valore è negativo (bit più significativo). Quindi, quando stampa, viene convertito in int
, e per mantenere l’equivalenza semantica, il compilatore riempie i byte extra con 0xff, quindi l’ int
negativo avrà lo stesso valore numerico del tuo char
negativo. Per risolvere questo problema, basta trasmettere a unsigned char
durante la stampa:
printf("%x", (unsigned char)variable);
Puoi usare hh
per dire a printf
che l’argomento è un char senza segno. Usa 0
per ottenere lo zero padding e 2
per impostare la larghezza su 2. x
o X
per caratteri esadecimali maiuscoli / minuscoli.
uint8_t a = 0x0a; printf("%02hhX", a); // Prints "0A" printf("0x%02hhx", a); // Prints "0x0a"
Modifica : Se i lettori sono preoccupati per l’asserzione di 2501 che questo non è in qualche modo gli specificatori di formato ‘corretto’, suggerisco di leggere di nuovo il link printf
. In particolare:
Anche se% c si aspetta un argomento int, è sicuro passare un char a causa della promozione intera che ha luogo quando viene chiamata una funzione variadic.
Le specifiche di conversione corrette per i tipi di carattere a larghezza fissa (int8_t, ecc.) Sono definite nell’intestazione
(C ++) o
(C) (sebbene PRIdMAX, PRIuMAX, ecc sia sinonimo di% jd,% ju, ecc . ) .
Per quanto riguarda il suo punto su firmato vs non firmato, in questo caso non importa in quanto i valori devono essere sempre positivi e facilmente inseribili in un int firmato. Non esiste comunque un identificatore di formato esagonale firmato.
Modifica 2 : (“quando-ammetti-hai sbagliato” edizione):
Se si legge l’effettivo standard C11 a pagina 311 (329 del PDF), si trova:
hh: specifica che un identificatore di conversione seguente
d
,i
,o
,u
,x
oX
applica a unsigned char
o a un argomentounsigned char
(l’argomento sarà promosso in base alle promozioni intere, ma il suo valore sarà convertito insigned char
ounsigned char
prima della stampa); o che unn
conversionen
successivo si applica a un puntatore a un argomentosigned char
.
Probabilmente stai stampando da un array di caratteri firmato. Stampare da un array di char senza segno o mascherare il valore con 0xff: ad esempio ar [i] e 0xFF. I valori c0 vengono firmati estesi perché il bit alto (segno) è impostato.
Prova qualcosa del genere:
int main() { printf("%x %x %x %x %x %x %x %x\n", 0xC0, 0xC0, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33); }
Che produce questo:
$ ./foo c0 c0 61 62 63 31 32 33