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?
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.
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
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);
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
ouX
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 conversionn
suivant s'applique à un pointeur sur un argument _signed char
_.
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é.