sintaxis libreria lenguaje imprimir formato especificadores ejemplos c printf variadic-functions promotions format-specifiers

c - libreria - printf(% d



¿Cuál es el propósito de los modificadores h y hh para printf? (7)

El único uso que se me ocurre es pasar un carácter unsigned short unsigned char o unsigned char y utilizar el especificador de conversión %x . No puede simplemente usar un %x desnudo: el valor puede promocionarse a int lugar de unsigned int , y luego tiene un comportamiento indefinido.

Sus alternativas son lanzar explícitamente el argumento a unsigned ; o para usar %hx / %hhx con un argumento %hhx .

Además de %hn y %hhn (donde h o hh especifica el tamaño del objeto apuntado ), ¿cuál es el sentido de los modificadores h y hh para los especificadores de formato printf ?

Debido a las promociones predeterminadas requeridas por la norma que se aplicará para las funciones variadic, es imposible pasar argumentos de tipo char o short (o cualquiera de las variantes firmadas / sin firmar de la misma) a printf .

De acuerdo con 7.19.6.1 (7), el modificador h :

Especifica que el siguiente especificador de conversión d, i, o, u, x o X se aplica a un argumento int breve corto o sin signo corto (el argumento se habrá promocionado de acuerdo con las promociones enteras, pero su valor se convertirá en short int o short sin signo antes de la impresión); o que un siguiente n especificador de conversión se aplica a un puntero a un breve argumento int.

Si el argumento era en realidad de tipo short o unsigned short , la promoción a int seguida de una conversión de nuevo a short o unsigned short arrojará el mismo valor que la promoción a int sin ninguna conversión de regreso. Por lo tanto, para los argumentos de tipo short o short unsigned short , %d , %u , etc. debería dar resultados idénticos a %hd , %hu , etc. (y lo mismo para los tipos de caracteres y hh ).

Por lo que puedo decir, la única situación donde el modificador h o hh podría ser útil es cuando el argumento pasa un int fuera del rango de short o unsigned short , por ejemplo

printf("%hu", 0x10000);

pero entiendo que pasar el tipo incorrecto como este da como resultado un comportamiento indefinido de todos modos, por lo que no se puede esperar que imprima 0.

Un caso del mundo real que he visto es un código como este:

char c = 0xf0; printf("%hhx", c);

donde el autor espera que se imprima f0 pesar de que la implementación tiene un tipo de char simple que está firmado (en cuyo caso, printf("%x", c) imprimiría fffffff0 o similar). Pero, ¿esta expectativa está garantizada?

(Nota: lo que sucede es que el tipo original era char , que se promociona a int y se convierte de nuevo a unsigned char lugar de char , cambiando así el valor que se imprime. Pero, ¿el estándar especifica este comportamiento o es una implementación? detalle que el software roto podría confiar?)


En el modo %...x , todos los valores se interpretan como sin signo. Por lo tanto, los números negativos se imprimen como sus conversiones sin firmar. En la aritmética de complemento a 2, que la mayoría de los procesadores usan, no hay diferencia en los patrones de bits entre un número negativo firmado y su equivalente positivo sin signo, que se define mediante la aritmética de módulo (agregando el valor máximo para el campo más uno al negativo, según al estándar C99). Gran cantidad de software, especialmente el código de depuración que con mayor probabilidad usará %x , asume silenciosamente que la representación de bit de un valor negativo firmado y su conversión sin signo es la misma, lo que solo es cierto en una máquina de complemento a 2.

La mecánica de este modelo es tal que las representaciones de valor hexadecimales siempre implican, posiblemente de forma incorrecta, que un número se ha procesado en complemento a 2, siempre que no haya alcanzado una condición de borde donde las diferentes representaciones enteras tienen rangos diferentes. Esto incluso es cierto para las representaciones aritméticas donde el valor 0 no se representa con el patrón binario de todos los 0.

Por lo tanto, un short negativo mostrado como un unsigned long en hexadecimal será rellenado con f , en cualquier máquina, debido a la extensión de signo implícita en la promoción, que printf imprimirá. El valor es el mismo, pero es visualmente engañoso en cuanto al tamaño del campo, lo que implica una cantidad significativa de rango que simplemente no está presente.

%hx trunca la representación mostrada para evitar este relleno, exactamente como concluyó en su caso de uso del mundo real.

El comportamiento de printf no está definido cuando pasa una int fuera del rango de short que debe imprimirse como un short , pero la implementación más sencilla simplemente descarta el bit alto por un downcast crudo, por lo tanto, mientras que la especificación no requiere ningún comportamiento específico , casi cualquier implementación sensata solo realizará el truncamiento. En general, hay mejores formas de hacerlo.

Si printf no está rellenando valores o mostrando representaciones sin firmar de valores con signo, %h no es muy útil.


Estoy de acuerdo con usted en que no es estrictamente necesario, y por eso solo no está bien en una función de biblioteca C :)

Puede ser "bueno" para la simetría de las diferentes banderas, pero es en su mayoría contraproducente porque oculta la regla de "conversión a int ".


Los argumentos variados para printf() y otros se promueven automáticamente utilizando las conversiones predeterminadas, por lo que cualquier valor short o de char se promueve a int cuando se pasa a la función.

En ausencia de los modificadores h o hh , debería enmascarar los valores pasados ​​para obtener el comportamiento correcto de manera confiable. Con los modificadores, ya no tiene que enmascarar los valores; la implementación de printf() hace el trabajo correctamente.

Específicamente, para el formato %hx , el código dentro de printf() puede hacer algo como:

va_list args; va_start(args, format); ... int i = va_arg(args, int); unsigned short s = (unsigned short)i; ...print s correctly, as 4 hex digits maximum ...even on a machine with 64-bit `int`!

Estoy alegremente asumiendo que el short es una cantidad de 16 bits; el estándar en realidad no garantiza eso, por supuesto.


Me resultó útil evitar la conversión al formatear caracteres sin signo en hexadecimal:

sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));

Es una conveniencia de codificación menor, y se ve más limpio que múltiples lanzamientos (IMO).


Otro lugar que es útil es la verificación de tamaño snprintf. gcc7 agregó una verificación de tamaño cuando se usa snprintf, por lo que esto fallará

char arr[4]; char x=''r''; snprintf(arr,sizeof(arr),"%d",r);

por lo que te obliga a usar caracteres más grandes cuando usas% d al formatear un carácter

Aquí hay una confirmación que muestra esas correcciones en lugar de aumentar el tamaño de la matriz char que cambiaron% d a% h. esto también da una descripción más precisa

https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa372068037fe161d24


Una posible razón: ¿para la simetría con el uso de esos modificadores en las funciones de entrada formateadas? Sé que no sería estrictamente necesario, pero tal vez haya valor visto por eso.

Aunque no mencionan la importancia de la simetría para los modificadores "h" y "hh" en el documento de C99 Rationale , el comité lo menciona como una consideración de por qué el especificador de conversión "% p" es compatible con fscanf() ( aunque eso no era nuevo para C99 - el soporte "% p" está en C90):

La conversión del puntero de entrada con% p se agregó a C89, aunque obviamente es arriesgado, por simetría con fprintf.

En la sección sobre fprintf() , el documento de justificación C99 sí fprintf() que se agregó "hh", pero simplemente remite al lector a la sección fscanf() :

Los modificadores% hh y% ll de longitud se agregaron en C99 (ver §7.19.6.2).

Sé que es un hilo tenue, pero estoy especulando de todos modos, así que pensé que daría cualquier argumento que pudiera haber.

Además, para completar, el modificador "h" estaba en el estándar original C89, presumiblemente estaría allí incluso si no fuera estrictamente necesario debido al uso existente generalizado, incluso si no hubiera habido un requisito técnico para usar el modificador. .