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 2 premiers caractères sont c0 en hex et les caractères restants sont abc123 en ASCII, alors je devrais obtenir

 c0 c0 61 62 63 31 32 33 

Cependant, printf 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 seuls c0 (et 80) ont le ffffff , mais pas les autres personnages?

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

Étant donné que c0 et 80 ont un bit de 1 en tête (et sont négatifs en tant qu’entier de 8 bits), ils sont étendus de signe alors que les autres de votre échantillon ne le font 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 souhaités.

En effet, il existe une conversion de type en int. De plus, vous pouvez forcer le type à utiliser le caractère en utilisant le spécificateur% hhx.

 printf("%hhX", a); 

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

 unsigned char c = 0xc5; 

L’impression donnera C5 et pas ffffffc5 .

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

Ou vous pouvez lancer le caractère lors de l’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 compresse les octets supplémentaires avec 0xff, de sorte que l’ int négatif aura la même valeur numérique que votre caractère négatif. Pour corriger cela, il suffit de lancer le unsigned char lors de l’impression:

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

Vous pouvez utiliser hh pour dire à printf que l’argument est un caractère non signé. Utilisez 0 pour obtenir un remplissage nul et 2 pour définir la largeur sur 2. x ou X pour les caractères hexadécimaux inférieurs / 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 des spécificateurs de format «corrects», je leur suggère de relire le lien printf . Plus précisément:

Même si% c attend un argument int, il est prudent de passer un caractère à cause de la promotion de nombre entier effectuée lors de l’appel d’une fonction variadic.

Les spécifications de conversion correctes pour les types de caractères à largeur fixe (int8_t, etc.) sont définies dans l’en-tête (C ++) ou (C) (bien que PRIdMAX, PRIuMAX, etc. soit synonyme de% jd,% ju, etc) .

Quant à son propos sur les signatures signées et les non signées, dans ce cas, peu importe, car les valeurs doivent toujours être positives et s’intégrer facilement dans un int signé. Il n’y a aucun spécificateur de format hexideximal signé de toute façon.

Edit 2 : ( édition “quand admettre que tu te trompes”):

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

hh: spécifie qu’un spécificateur de conversion d , i , o , u , x ou X s’applique à un argument signed char ou unsigned char (l’argument aura été promu en fonction des promotions entières, mais sa 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 .

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 étendues en signe car le bit haut (signe) est défini.

Essayez quelque chose comme ça:

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

Qui produit ceci:

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