printf agrega `FFFFFF` extra a la impresión hexadecimal de una matriz char

Considere el siguiente código simplificado a continuación. Quiero extraer algunos datos / flujos binarios de un archivo e imprimirlos en la salida estándar en formato hexadecimal.

0xFFFFFF 3 bytes adicionales 0xFFFFFF . ¿Qué pasa? ¿De dónde vinieron los bytes adicionales?

salida

 in: 2000FFFFFFAF00690033005A00 out: 2000FFFFFFAF00690033005A00 

progtwig.c

 #include  #include  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; } 

Extensión de la señal Tu comstackdor está implementando char como signed char . Cuando pasa los caracteres a printf , todos se muestran extendidos durante su promoción a int . Cuando el primer bit es un 0, 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 por lo que es 11111111111111111111111110101111 , el valor hexadecimal que tiene.

Solución: en lugar de eso, use unsigned char para evitar que se produzca el formulario de extensión de 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 están siendo extendidos, es solo que 0xAF es el único con un 1 en el primer bit.

Otro ejemplo más simple del mismo comportamiento

 signed char c = 0xAF; // probably gives an overflow warning int i = c; // extra 24 bits are all 1 assert( i == 0xFFFFFFAF ); 

Esto se debe a que 0xAF cuando se convierte de un carácter firmado a un entero con signo es negativo (es signo extendido), y el formato %02X es para argumentos sin firmar e imprime el valor convertido como FFFFFFAF .

Los caracteres adicionales aparecen porque printf %x nunca truncará los dígitos de un valor de forma silenciosa. Los valores que no son negativos también reciben señal extendida, pero eso es simplemente agregar cero bits y el valor se ajusta 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 en el que se ha firmado un char simple y otro en el que no está firmado. En el tuyo está firmado. Puede cambiarlo usando una opción, por ejemplo, gcc y soporte de clang -funsigned-char y -fsigned-char .

El printf() es una función variadica y sus argumentos adicionales (que corresponden con ... parte de su prototipo) están sujetos a promociones de argumento predeterminadas , por lo que char se promueve a int .

Como su char ha firmado 1 , la representación de dos complementos, el bit más significativo se establece en uno para el elemento 0xAF . Durante la promoción, el bit firmado se propaga, lo que 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 usar para el objeto de tipo unsigned int o al menos para int con MSB que está desarmado (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) Implementación puede elegir entre representación firmada y no firmada de char . Es bastante común que char esté firmado, pero no se puede dar por sentado para cualquier otro comstackdor del planeta. Algunos de ellos pueden permitir elegir entre estos dos modos, como se menciona en la respuesta de Jens .