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.