Imprimir caracteres hexadecimales en C
Estoy intentando leer una línea de caracteres y luego imprimir el equivalente hexadecimal de los caracteres.
Por ejemplo, si tengo una cadena donde "0xc0 0xc0 abc123"
los primeros 2 caracteres están c0
en hexadecimal y los caracteres restantes están abc123
en ASCII, entonces debería obtener
c0 c0 61 62 63 31 32 33
Sin embargo, printf
usar %x
me da
ffffffc0 ffffffc0 61 62 63 31 32 33
¿Cómo obtengo el resultado que quiero sin "ffffff"
? ¿Y por qué solo c0 (y 80) tiene el ffffff
, pero no los demás caracteres?
Estás viendo el ffffff
porque char
está firmado en tu sistema. En C, funciones vararg como printf
promoverán todos los números enteros más pequeños que int
to int
. Dado que char
es un número entero (un entero con signo de 8 bits en su caso), sus caracteres se promocionan a int
través de una extensión de signo.
Dado que c0
y 80
tienen un bit inicial (y son negativos como un entero de 8 bits), se les extiende el signo mientras que los demás en su muestra no.
char int
c0 -> ffffffc0
80 -> ffffff80
61 -> 00000061
Aquí tienes una solución:
char ch = 0xC0;
printf("%x", ch & 0xff);
Esto enmascarará los bits superiores y mantendrá solo los 8 bits inferiores que desee.
De hecho, existe una conversión de tipos a int. También puede forzar el tipo a char usando el especificador %hhx.
printf("%hhX", a);
En la mayoría de los casos, también querrás establecer la longitud mínima para llenar el segundo carácter con ceros:
printf("%02hhX", a);
ISO/IEC 9899:201x dice:
7 Los modificadores de longitud y sus significados son: hh Especifica que el siguiente especificador de conversión d, i, o, u, x o X se aplica a un argumento char con signo o char sin firmar (el argumento se habrá promovido de acuerdo con las promociones de enteros, pero su valor se convertirá a char firmado o char sin firmar antes de imprimir); o que un seguidor
Puedes crear un carácter sin firmar:
unsigned char c = 0xc5;
Imprimirlo dará C5
y no ffffffc5
.
Sólo los caracteres mayores a 127 se imprimen con ffffff
porque son negativos (el carácter está firmado).
O puede transmitir char
mientras imprime:
char c = 0xc5;
printf("%x", (unsigned char)c);
Puede utilizar hh
para saber printf
que el argumento es un carácter sin firmar. Úselo 0
para obtener relleno cero y 2
establecer el ancho en 2. x
o X
para caracteres hexadecimales en minúsculas/mayúsculas.
uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"
Editar : Si a los lectores les preocupa la afirmación de 2501 de que de alguna manera estos no son los especificadores de formato "correctos", les sugiero que lean el printf
enlace nuevamente. Específicamente:
Aunque %c espera un argumento int, es seguro pasar un char debido a la promoción de enteros que tiene lugar cuando se llama a una función variable.
Las especificaciones de conversión correctas para los tipos de caracteres de ancho fijo (int8_t, etc.) se definen en el encabezado
<cinttypes>
(C++) o<inttypes.h>
(C) (aunque PRIdMAX, PRIuMAX, etc. es sinónimo de %jd, %ju, etc.) .
En cuanto a su punto sobre firmado versus sin firmar, en este caso no importa ya que los valores siempre deben ser positivos y caber fácilmente en un int con signo. De todos modos, no existe un especificador de formato hexideximal firmado.
Edición 2 : (edición "cuándo-admitir-que-estás-equivocado"):
Si lee el estándar C11 real en la página 311 (329 del PDF), encontrará:
hh: Especifica que el siguiente especificador de conversión
d
,i
,o
,u
,x
oX
se aplica a un argumentosigned char
ounsigned char
(el argumento se habrá promovido de acuerdo con las promociones de enteros, pero su valor se convertirá asigned char
ounsigned char
antes de imprimir); o que el siguienten
especificador de conversión se aplique a un puntero a unsigned char
argumento.