printf agrega `FFFFFF` extra a la impresión hexadecimal desde una matriz de caracteres
arrays (3)
Esta pregunta ya tiene una respuesta aquí:
Considere el siguiente código simplificado a continuación. Quiero extraer algunos datos / secuencias binarias de un archivo e imprimirlo en la salida estándar en formato hexadecimal.
Tengo 3 bytes adicionales
0xFFFFFF
.
Que pasa
¿De dónde vinieron los bytes adicionales?
salida
in:
2000FFFFFFAF00690033005A00
out:
2000FFFFFFAF00690033005A00
programa.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int i;
char raw[10] = {0x20,0x00,0xAF,0x00,0x69,0x00,0x33,0x00,0x5A,0x00};
FILE *outfile;
char *buf;
printf("in:/n/t");
for( i=0; i<10; i++ )
printf("%02X", raw[i]);
outfile = fopen("raw_data.bin", "w+b");
fwrite(raw, 1, 10, outfile);
buf = (char *) malloc (32 * sizeof(char));
fseek(outfile, 0, SEEK_SET);
fread(buf, 1, 10, outfile);
printf("/nout:/n/t");
for( i=0; i<10; i++ )
printf("%02X", buf[i]);
printf("/n");
fclose(outfile);
return 0;
}
Esto se debe a que 0xAF cuando se convierte de un carácter con signo a un entero con signo es negativo (es un signo extendido), y el formato
%02X
es para argumentos sin signo e imprime el valor convertido como
FFFFFFAF
.
Los caracteres adicionales aparecen porque printf
%x
nunca
truncará silenciosamente los dígitos de un valor.
Los valores que no son negativos también obtienen signos extendidos, pero eso es solo agregar cero bits y el valor cabe en 2 dígitos hexadecimales, por lo que printf
%02
puede hacerlo con una salida de dos dígitos.
Tenga en cuenta que hay 2 dialectos en C: uno donde el
char
simple está firmado y otro donde no está firmado.
En el tuyo está firmado.
Puede cambiarlo usando una opción, por ejemplo, gcc y clang support
-funsigned-char
y
-fsigned-char
.
Extensión de señal.
Su compilador está implementando
char
como un
signed char
.
Cuando pasa los caracteres a
printf
, todos se extienden durante su promoción a
int
s.
Cuando el primer bit es un 0, esto no importa, porque se extiende con
0
s.
0xAF
en binario es
10101111
Dado que el primer bit es un
1
, al pasarlo a
printf
se extiende con todos los
1
s en la conversión a
int
lo que lo hace
11111111111111111111111110101111
, el valor hexadecimal que tiene.
Solución: en su
lugar, use el
unsigned char
para evitar que se produzca la extensión del signo en la llamada
const unsigned char raw[] = {0x20,0x00,0xAF,0x00,0x69,0x00,0x33,0x00,0x5A,0x00};
Todos estos valores en su ejemplo original se están extendiendo, es solo que
0xAF
es el único con un
1
en el primer bit.
Otro ejemplo más simple del mismo comportamiento ( enlace en vivo ):
signed char c = 0xAF; // probably gives an overflow warning
int i = c; // extra 24 bits are all 1
assert( i == 0xFFFFFFAF );
printf()
es una función
variada
y sus argumentos adicionales (que corresponden con
...
parte de su prototipo) están sujetos a
promociones de argumentos por defecto
, por lo tanto
char
se promociona a
int
.
Como su
char
ha firmado
1
,
la
representación
del complemento de dos,
el bit más significativo, se establece en uno para el elemento
0xAF
.
Durante la promoción, el bit firmado se propaga, dando
0xFFFFFFAF
resultado
0xFFFFFFAF
de tipo
int
, como presumiblemente
sizeof(int) = 4
en su implementación.
Por cierto, está invocando
un comportamiento indefinido
, ya que el especificador de formato
%X
debe usarse para objetos de tipo
unsigned int
o al menos para
int
con MSB que no está establecido (esta es una práctica común y ampliamente aceptada).
Como se sugiere, puede considerar el uso de un tipo de caracteres sin
unsigned char
inequívoco.
1) La implementación puede elegir entre representación firmada y no firmada de
char
.
Es bastante común que
char
esté firmado, pero no puedes darlo por sentado para cualquier otro compilador del planeta.
Algunos de ellos pueden permitir elegir entre estos dos modos, como se menciona en
la respuesta de Jens
.