c arrays printf

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 .