web-dev-qa-db-fra.com

Impression de caractères hexadécimaux en C

J'essaie de lire une ligne de caractères, puis d'imprimer l'équivalent hexadécimal des caractères.

Par exemple, si j'ai une chaîne qui est "0xc0 0xc0 abc123", où les deux premiers caractères sont c0 en hexadécimal et les caractères restants sont abc123 en ASCII, alors je devrais obtenir

c0 c0 61 62 63 31 32 33

Cependant, printf en utilisant %x me donne

ffffffc0 ffffffc0 61 62 63 31 32 33

Comment puis-je obtenir le résultat souhaité sans le "ffffff"? Et pourquoi est-ce que seulement c0 (et 80) a le ffffff, mais pas les autres caractères?

84
Rayne

Vous voyez la ffffff parce que char est signé sur votre système. En C, les fonctions vararg telles que printf promeuvent tous les entiers inférieurs à int à int. Puisque char est un entier (entier signé de 8 bits dans votre cas), vos caractères sont promus en int via l'extension de signe.

Étant donné que c0 et 80 ont un 1 bit (et sont négatifs comme un entier de 8 bits), ils sont étendus, alors que les autres de votre exemple ne le sont pas.

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

Voici une solution:

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

Cela masquera les bits supérieurs et ne conservera que les 8 bits inférieurs que vous souhaitez.

112
Mysticial

En effet, il existe une conversion de type en int. Vous pouvez également forcer le type à char en utilisant le spécificateur% hhx.

printf("%hhX", a);

Dans la plupart des cas, vous voudrez également définir la longueur minimale pour remplir le second caractère avec des zéros:

printf("%02hhX", a);

ISO/IEC 9899: 201x dit:

7 Les modificateurs de longueur et leur signification sont les suivants: hh Spécifie qu'un spécificateur de conversion d, i, o, u, x ou X suivant s'applique à un argument de caractère signé ou non signé (l'argument aura été promu en fonction des promotions d'entier, mais sa valeur doit être convertie en caractère signé ou en caractère non signé avant impression); ou qu'une suite

55
brutal_lobster

Vous pouvez créer un caractère non signé:

unsigned char c = 0xc5;

L'imprimer donnera C5 et non ffffffc5.

Seuls les caractères supérieurs à 127 sont imprimés avec le ffffff car ils sont négatifs (le caractère est signé).

Ou vous pouvez convertir la char en cours d’impression:

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

Vous stockez probablement la valeur 0xc0 dans une variable char, ce qui est probablement un type signé, et votre valeur est négative (le bit le plus significatif est défini). Ensuite, lors de l’impression, il est converti en int, et pour conserver l’équivalence sémantique, le compilateur complète les octets supplémentaires avec 0xff. Ainsi, le négatif int aura la même valeur numérique que votre négatif char. Pour résoudre ce problème, il suffit de transtyper vers unsigned char lors de l'impression:

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

Vous pouvez utiliser hh pour indiquer printf que l'argument est un caractère non signé. Utilisez _0_ pour obtenir un zéro et _2_ pour définir la largeur sur 2. x ou X pour les caractères hexadécimaux minuscules/majuscules.

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

Edit : Si les lecteurs sont préoccupés par l'affirmation de 2501 selon laquelle il ne s'agit pas du tout des spécificateurs de format "corrects", je suggère qu'ils lisent le lien printf encore. Plus précisément:

Même si% c attend l'argument int, il est prudent de passer un caractère à cause de la promotion d'entier qui a lieu lorsqu'une fonction variadique est appelée.

Les spécifications de conversion correctes pour les types de caractère de largeur fixe (int8_t, etc.) sont définies dans l'en-tête _<cinttypes>_ (C++) ou _<inttypes.h>_ (C) (bien que PRIdMAX , PRIuMAX, etc. est synonyme de% jd,% ju, etc.) .

Quant à son argument sur signés vs non signés, peu importe dans ce cas, les valeurs doivent toujours être positives et s'intégrer facilement dans un int signé. De toute façon, il n’existe pas de spécificateur de format hexideximal signé.

Edit 2 : (édition "Quand admettre-vous-vous-vous-trompez"):

Si vous lisez la norme C11 actuelle à la page 311 (329 du PDF), vous trouverez:

hh: Spécifie qu'un spécificateur de conversion d, i, o, u, x ou X suivant s'applique à un argument _signed char_ ou _unsigned char_ (l'argument aura été promu conformément à la promotion entière, mais sa la valeur doit être convertie en _signed char_ ou _unsigned char_ avant impression); ou qu'un spécificateur de conversion n suivant s'applique à un pointeur sur un argument _signed char_.

11
Timmmm

Vous imprimez probablement à partir d'un tableau de caractères signé. Imprimez à partir d'un tableau de caractères non signé ou masquez la valeur avec 0xff: par exemple. ar [i] & 0xFF. Les valeurs c0 sont en train d'être étendues, car le bit haut (signe) est activé.

2