Stampa di caratteri esadecimali in C


103

Sto cercando di leggere una riga di caratteri, quindi stampare l'equivalente esadecimale dei caratteri.

Ad esempio, se ho una stringa che è "0xc0 0xc0 abc123", dove i primi 2 caratteri sono c0in esadecimale e i caratteri rimanenti sono abc123in ASCII, allora dovrei ottenere

c0 c0 61 62 63 31 32 33

Tuttavia, l' printfutilizzo %xmi dà

ffffffc0 ffffffc0 61 62 63 31 32 33

Come ottengo l'output che desidero senza "ffffff"? E perché solo c0 (e 80) ha i caratteri ffffff, ma non gli altri?


La stringa che corrisponde al tuo array di byte sarebbe ..."\xc0\xc0abc123"
burito

Risposte:


132

Stai vedendo il ffffffperché charè firmato sul tuo sistema. In C, le funzioni vararg come printfpromuoveranno tutti i numeri interi inferiori inta int. Poiché charè un numero intero (numero intero intcon segno a 8 bit nel tuo caso), i tuoi caratteri vengono promossi tramite l'estensione del segno.

Poiché c0e 80hanno un 1 bit iniziale (e sono negativi come un intero a 8 bit), vengono estesi con il segno mentre gli altri nel campione no.

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 inferiori desiderati.


15
La mia soluzione usando un cast unsigned charè un'istruzione più piccola in gcc4.6 per x86-64 ...
lvella

1
Forse posso aiutarti. Questo è (tecnicamente) un comportamento indefinito perché lo specificatore xrichiede un tipo senza segno, ma ch è promosso a int. Il codice corretto sarebbe semplicemente gettato ch a unsigned, o utilizzare un cast per unsigned char e l'identificatore: hhx.
2501

1
In caso affermativo printf("%x", 0), non viene stampato nulla.
Gustavo Meira

Non stampa nulla perché il minimo è impostato a 0. Per risolvere questo problema, prova printf("%.2x", 0);a incrementare i caratteri minimi disegnati a 2. Per impostare un massimo, anteponi il. con un numero. Ad esempio, puoi forzare solo 2 caratteri disegnati facendoprintf("%2.2x", 0);
user2262111

Qualche motivo per cui printf("%x", ch & 0xff)dovrebbe essere migliore del semplice utilizzo printf("%02hhX", a)come nella risposta di @ brutal_lobster ?
maxschlepzig

62

In effetti, esiste la conversione del tipo in int. Inoltre puoi forzare il tipo a char usando l'identificatore% hhx.

printf("%hhX", a);

Nella maggior parte dei casi vorrai impostare anche la lunghezza minima per riempire il secondo carattere con zeri:

printf("%02hhX", a);

ISO / IEC 9899: 201x dice:

7 I modificatori di lunghezza e il loro significato sono: hh Specifica che un seguente specificatore di conversione d, i, o, u, x o X si applica a un argomento char con segno o char non firmato (l'argomento sarà stato promosso in base alle promozioni intere, ma il suo valore deve essere convertito in caratteri con segno o caratteri senza segno prima della stampa); o che un seguito


30

Puoi creare un carattere non firmato:

unsigned char c = 0xc5;

La stampa darà C5e non ffffffc5.

Solo i caratteri maggiori di 127 vengono stampati con il ffffffperché sono negativi (il carattere è firmato).

Oppure puoi lanciare chardurante la stampa:

char c = 0xc5; 
printf("%x", (unsigned char)c);

3
+1 la risposta migliore reale, digitando esplicitamente il più vicino possibile alla dichiarazione dei dati (ma non più vicino).
Bob Stein

13

Probabilmente stai memorizzando il valore 0xc0 in una charvariabile, probabilmente un tipo con segno , e il tuo valore è negativo (set di bit più significativo). Quindi, durante la stampa, viene convertito in inte per mantenere l'equivalenza semantica, il compilatore riempie i byte extra con 0xff, quindi il negativo intavrà lo stesso valore numerico del tuo negativo char. Per risolvere questo problema, eseguire il cast unsigned chardurante la stampa:

printf("%x", (unsigned char)variable);

13

Puoi usare hhper dire printfche l'argomento è un carattere senza segno. Utilizzare 0per ottenere spaziatura zero e 2impostare la larghezza su 2. xo Xper caratteri esadecimali minuscoli / maiuscoli.

uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"

Modifica : se i lettori sono preoccupati per l'affermazione di 2501 secondo cui questo non è in qualche modo gli specificatori di formato "corretti", suggerisco di leggere di nuovo il printfcollegamento . Nello specifico:

Anche se% c si aspetta un argomento int, è sicuro passare un carattere a causa della promozione del numero intero che si verifica quando viene chiamata una funzione variadica.

Le specifiche di conversione corrette per i tipi di caratteri a larghezza fissa (int8_t, ecc.) Sono definite nell'intestazione <cinttypes>(C ++) o <inttypes.h>(C) (sebbene PRIdMAX, PRIuMAX, ecc. Sia sinonimo di% jd,% ju, ecc . ) .

Per quanto riguarda il suo punto su signed vs unsigned, in questo caso non importa poiché i valori devono essere sempre positivi e adattarsi facilmente a un int con segno. Non esiste comunque alcun identificatore di formato esidimale con segno.

Modifica 2 : (edizione "quando-ammettere-ti-sbagli"):

Se leggi l'attuale standard C11 a pagina 311 (329 del PDF) troverai:

hh: Specifica che un seguito d, i, o, u, x, o Xconversione specificatore applica ad una signed charo unsigned charargomento (l'argomento sarà stato promosso secondo promozioni interi, ma il suo valore è convertito signed charo unsigned charprima della stampa); o che un seguente nspecificatore di conversione si applica a un puntatore a un signed charargomento.


Gli specificatori non sono corretti per il tipo uint8_t. I tipi di larghezza fissa utilizzano specificatori di stampa speciali. Vedi:inttypes.h
2501

Sì, ma tutti i valori interi vararg sono promossi implicitamente a int.
Timmmm

Può essere, ma per quanto C è definito, il comportamento non è definito se non si utilizza lo specificatore corretto.
2501

Ma% x è l'identificatore corretto. ( chare unsigned charsono promossi a int) [ en.cppreference.com/w/cpp/language/variadic_arguments] . Avresti solo bisogno di utilizzare gli specificatori PRI per le cose che non si adattano alla tua piattaforma int, ad es unsigned int.
Timmmm

%xè corretto per unsigned int non int. I tipi char e unsigned char vengono promossi a int. Inoltre non vi è alcuna garanzia che uint8_t sia definito come unsigned char.
2501

2

Probabilmente stai stampando da un array di caratteri firmato. Stampa da un array di caratteri non firmato o maschera il valore con 0xff: ad esempio ar [i] & 0xFF. I valori c0 vengono estesi con il segno perché è impostato il bit alto (segno).


-1

Prova qualcosa di simile:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.