Especificador de formato para char sin signo

Digamos que quiero imprimir unsigned char :

 unsigned char x = 12; 

cual es correcta. Esta:

 printf("%d",x); 

o esto:

 printf("%u",x); 

?

El asunto está en otra parte de SO He encontrado tal discusión:

– Incluso con ch cambiado a char sin signo, el comportamiento del código no está definido por el estándar C. Esto se debe a que la char sin signo se promueve a una int (en implementaciones C normales), por lo que se pasa una int a printf para la especificador% u. Sin embargo,% u espera una int sin firmar, por lo que los tipos no coinciden, y el estándar C no define el comportamiento

-Su comentario es incorrecto. El estándar C11 establece que el especificador de conversión debe ser del mismo tipo que el argumento de función en sí, no el tipo promocionado. Este punto también se trata específicamente en la descripción del modificador de longitud hh: “el argumento se habrá promocionado de acuerdo con las promociones enteras, pero su valor se convertirá en char firmado o carácter no firmado antes de imprimir”

Entonces, ¿cuál es correcto? ¿Alguna fuente confiable que dice sobre este asunto? (En ese sentido, también deberíamos imprimir unsigned short int con% d porque puede promocionarse a int ?).

El correcto es *:

 printf("%d",x); 

Esto se debe a las promociones de argumentos por defecto, ya que printf() es una función variadica. Esto significa que el valor unsigned char siempre se promueve a int .

Desde N1570 (borrador C11) 6.5.2.2/6 Llamadas de función (énfasis mío en el futuro):

Si la expresión que denota la función llamada tiene un tipo que no incluye un prototipo, las promociones enteras se llevan a cabo en cada argumento, y los argumentos que tienen tipo float se promueven en el double . Estas se llaman promociones de argumento predeterminadas .

y la subcláusula 6.5.2.2/7 dice:

La notación de puntos suspensivos en un declarador de prototipo de función hace que la conversión del tipo de argumento se detenga después del último parámetro declarado. Las promociones de argumento predeterminadas se realizan en argumentos finales .

Estas promociones enteras se definen en 6.3.1.1/2 booleanos, caracteres y enteros :

Si un int puede representar todos los valores del tipo original (como está restringido por el ancho, para un campo de bit), el valor se convierte en un int ; de lo contrario, se convierte a unsigned int . Estas se llaman promociones enteras .58) Los demás tipos no se modifican por las promociones enteras.

Esta cita responde a su segunda pregunta de unsigned short (vea el comentario a continuación).


* con excepción de más de 8 bits de caracteres unsigned char (por ejemplo, podría ocupar 16 bits), vea la respuesta de @ chux.

El especificador de formato correcto para unsigned char x = 12 depende de varias cosas:

Si INT_MAX >= UCHAR_MAX , que a menudo es el caso, use "%d" . En este caso, un unsigned char se promueve a int .

 printf("%d",x); 

De lo contrario, use "%u" (o "%x" , "%o" ). En este caso, un unsigned char se promueve a unsigned .

 printf("%u",x); 

Los comstackdores actualizados admiten el modificador de longitud "hh" , que compensa esta ambigüedad. En caso de que x sea ​​promovido a int o unsigned debido a las promociones estándar de parámetros variados, printf() convierte en unsigned char antes de imprimir.

 printf("%hhu",x); 

Si se trata de un comstackdor antiguo sin "hh" o que busca un código altamente portátil, use casting explícito

 printf("%u", (unsigned) x); 

El mismo problema / respuesta se aplica al unsigned short , espere INT_MAX >= USHRT_MAX y use "h" lugar de "hh" .

Ambos, unsigned char y unsigned short , siempre se pueden imprimir de forma segura con %u . Las promociones predeterminadas de argumentos las convierten a int o a unsigned int . Si se promocionan a este último, todo está bien (el especificador de formato y el tipo de coincidencia pasada); de lo contrario, C11 (n1570) 6.5.2.2 p6, primera viñeta, se aplica:

  • un tipo promovido es un tipo entero con signo, el otro tipo promovido es el tipo entero sin signo correspondiente, y el valor es representable en ambos tipos;

El estándar es bastante claro que las promociones de argumento predeterminadas se aplican a los argumentos variados de printf , por ejemplo, se menciona nuevamente para los modificadores de longitud h y hh (mayormente inútiles) (ibid. 7.21.6.1 p7, emph. Mine):

hh : especifica que el siguiente especificador de conversión d , i , o , u , x o X aplica a un argumento unsigned char o unsigned char ( el argumento se habrá promocionado de acuerdo con las promociones enteras , pero su valor se convertirá en signed char o unsigned char antes de imprimir); […]

Para el desarrollo multiplataforma, generalmente puenteo el problema de promoción mediante el uso de inttypes.h

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/inttypes.h.html

Este encabezado (que está en el estándar C99) define todos los tipos de printf para los tipos básicos. Entonces, si quieres un uint8_t (una syntax que recomiendo utilizar en lugar de un carácter sin signo), usaría

 #include  #include  uint8_t x; printf("%" PRIu8 "\n",x);