Hexadezimalzeichen in C drucken

103

Ich versuche, eine Zeichenzeile einzulesen und dann das hexadezimale Äquivalent der Zeichen auszudrucken.

Wenn ich zum Beispiel eine Zeichenfolge habe "0xc0 0xc0 abc123", bei der die ersten beiden Zeichen c0hexadezimal und die restlichen Zeichen abc123in ASCII sind, sollte ich sie erhalten

c0 c0 61 62 63 31 32 33

Die printfVerwendung %xgibt mir jedoch

ffffffc0 ffffffc0 61 62 63 31 32 33

Wie bekomme ich die gewünschte Ausgabe ohne die "ffffff"? Und warum hat nur c0 (und 80) die ffffff, aber nicht die anderen Zeichen?

Rayne
quelle
Die Zeichenfolge, die Ihrem Array von Bytes entspricht, wäre ..."\xc0\xc0abc123"
Burito

Antworten:

132

Sie sehen das, ffffffweil charauf Ihrem System signiert ist. In C werden mit vararg-Funktionen printfalle Ganzzahlen heraufgestuft , die kleiner als intbis sind int. Da chares sich um eine Ganzzahl handelt (in Ihrem Fall eine 8-Bit-Ganzzahl mit Vorzeichen), werden Ihre Zeichen intüber die Vorzeichenerweiterung heraufgestuft.

Da c0und 80ein führendes 1-Bit haben (und als 8-Bit-Ganzzahl negativ sind), werden sie vorzeichenerweitert, während die anderen in Ihrem Beispiel dies nicht tun.

char    int
c0 -> ffffffc0
80 -> ffffff80
61 -> 00000061

Hier ist eine Lösung:

char ch = 0xC0;
printf("%x", ch & 0xff);

Dadurch werden die oberen Bits ausgeblendet und nur die unteren 8 Bits beibehalten, die Sie möchten.

Mystisch
quelle
15
Meine Lösung mit einem Cast to unsigned charist eine Anweisung kleiner in gcc4.6 für x86-64 ...
lvella
1
Vielleicht kann ich helfen. Dies ist (technisch) undefiniertes Verhalten, da der Bezeichner xeinen vorzeichenlosen Typ erfordert, ch jedoch zu int heraufgestuft wird. Der richtige Code würde einfach ch in unsigned umwandeln oder eine Umwandlung in unsigned char und den Bezeichner verwenden : hhx.
2501
1
Wenn ja printf("%x", 0), wird nichts gedruckt.
Gustavo Meira
Es wird nichts gedruckt, da das Minimum auf 0 gesetzt ist. Um dies zu beheben, versuchen Sie printf("%.2x", 0);, die minimalen Zeichen auf 2 zu erhöhen. Um ein Maximum festzulegen, stellen Sie das vor. mit einer Nummer. Zum Beispiel können Sie nur 2 Zeichen erzwingen, indem Sie tunprintf("%2.2x", 0);
user2262111
Gibt es einen Grund, warum printf("%x", ch & 0xff)es besser sein sollte, als nur printf("%02hhX", a)die Antwort von @ brutal_lobster zu verwenden ?
Maxschlepzig
62

In der Tat gibt es eine Typkonvertierung in int. Sie können den Typ auch erzwingen, indem Sie den% hhx-Bezeichner verwenden.

printf("%hhX", a);

In den meisten Fällen möchten Sie auch die Mindestlänge festlegen, um das zweite Zeichen mit Nullen zu füllen:

printf("%02hhX", a);

ISO / IEC 9899: 201x sagt:

7 Die Längenmodifikatoren und ihre Bedeutung sind: hh Gibt an, dass ein folgender Konvertierungsspezifizierer für d, i, o, u, x oder X für ein vorzeichenbehaftetes Zeichen oder ein vorzeichenloses Zeichenargument gilt (das Argument wurde gemäß den ganzzahligen Beförderungen heraufgestuft). aber sein Wert wird vor dem Drucken in signiertes oder nicht signiertes Zeichen umgewandelt; oder dass eine folgende

brutal_lobster
quelle
30

Sie können ein vorzeichenloses Zeichen erstellen:

unsigned char c = 0xc5;

Drucken wird es geben C5und nicht ffffffc5.

Nur die Zeichen, die größer als 127 sind, werden mit dem Zeichen gedruckt, ffffffda sie negativ sind ( Zeichen ist signiert).

Oder Sie können das charbeim Drucken gießen :

char c = 0xc5; 
printf("%x", (unsigned char)c);
Hicham
quelle
3
+1 die wirklich beste Antwort, explizite Eingabe so nah wie möglich an der Datendeklaration (aber nicht näher).
Bob Stein
13

Sie speichern wahrscheinlich den Wert 0xc0 in einer charVariablen, was wahrscheinlich ein vorzeichenbehafteter Typ ist, und Ihr Wert ist negativ (höchstwertiges Bit gesetzt). Beim Drucken wird es dann in konvertiert int, und um die semantische Äquivalenz beizubehalten, füllt der Compiler die zusätzlichen Bytes mit 0xff auf, sodass das Negativ intden gleichen numerischen Wert wie Ihr Negativ hat char. Um dies zu beheben, werfen Sie unsigned charbeim Drucken einfach auf :

printf("%x", (unsigned char)variable);
lvella
quelle
13

Sie können hhdamit feststellen, printfdass das Argument ein vorzeichenloses Zeichen ist. Verwenden Sie 0diese Option, um einen Nullpunkt zu erhalten und 2die Breite auf 2 xoder Xfür Hex-Zeichen in Klein- / Großbuchstaben festzulegen.

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

Bearbeiten : Wenn die Leser über die Behauptung von 2501 besorgt sind, dass dies irgendwie nicht die "richtigen" Formatspezifizierer sind, schlage ich vor, dass sie den printfLink erneut lesen . Speziell:

Obwohl% c ein int-Argument erwartet, ist es sicher, ein Zeichen zu übergeben, da beim Aufruf einer variadischen Funktion eine Ganzzahl-Heraufstufung stattfindet.

Die korrekten Konvertierungsspezifikationen für die Zeichentypen mit fester Breite (int8_t usw.) sind im Header <cinttypes>(C ++) oder <inttypes.h>(C) definiert (obwohl PRIdMAX, PRIuMAX usw. gleichbedeutend mit% jd,% ju usw. sind) .

In diesem Fall spielt es keine Rolle, ob die Werte signiert oder nicht signiert sind, da die Werte immer positiv sein müssen und leicht in ein signiertes Int passen. Es gibt sowieso keinen vorzeichenbehafteten hexideximalen Formatbezeichner.

Edit 2 : (Ausgabe "Wann muss man zugeben, dass man falsch liegt?"):

Wenn Sie den aktuellen C11-Standard auf Seite 311 (329 des PDF) lesen , finden Sie:

hh: Gibt an, dass eine nachfolgende d, i, o, u, x, oder XKonvertierungsspezifizierer zu a gilt signed charoder unsigned charArgument (das Argument wird gemäß den Integer - aktionen gefördert worden, aber sein Wert wird umgewandelt werden signed charoder unsigned charvor dem Drucken); oder dass ein folgender nKonvertierungsspezifizierer auf einen Zeiger auf ein signed charArgument angewendet wird.

Timmmm
quelle
Die Bezeichner sind für den Typ uint8_t nicht korrekt. Bei Typen mit fester Breite werden spezielle Druckspezifizierer verwendet. Siehe:inttypes.h
2501
Ja, aber alle varargs-Ganzzahlen werden implizit zu int heraufgestuft.
Timmmm
Das mag sein, aber soweit C definiert ist, ist das Verhalten undefiniert, wenn Sie nicht den richtigen Bezeichner verwenden.
2501
% X ist jedoch der richtige Bezeichner. ( charund unsigned charwerden befördert zu int) [ en.cppreference.com/w/cpp/language/variadic_arguments] . Sie müssten die PRI-Spezifizierer nur für Dinge verwenden, die nicht in Ihre Plattform passen int- z unsigned int.
Timmmm
%xist korrekt für unsigned int not int. Die Typen char und unsigned char werden zu int heraufgestuft. Darüber hinaus gibt es keine Garantie dafür, dass uint8_t als vorzeichenloses Zeichen definiert ist.
2501
2

Sie drucken wahrscheinlich von einem signierten char-Array. Entweder aus einem vorzeichenlosen Zeichenarray drucken oder den Wert mit 0xff maskieren: zB ar [i] & 0xFF. Die c0-Werte werden vorzeichenerweitert, da das hohe (Vorzeichen-) Bit gesetzt ist.

Richard Pennington
quelle
-1

Versuchen Sie so etwas:

int main()
{
    printf("%x %x %x %x %x %x %x %x\n",
        0xC0, 0xC0, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33);
}

Welches produziert dies:

$ ./foo 
c0 c0 61 62 63 31 32 33
ObscureRobot
quelle