MinGW GCC: “carácter de tipo de conversión desconocido ‘h'” (snprintf)

De acuerdo, me he encontrado con un problema extraño al comstackr un archivo C con MinGW (GCC 4.6.2) en Windows 7. El archivo en cuestión contiene el siguiente código C:

#include  int main(int argc, char *argv[]) { printf("%2hhX\n", 250); char c[80]; snprintf(c, sizeof(c), "%2hhX", 250); printf("%s\n", c); return 0; } 

La comstackción resulta así:

 $ gcc.exe -std=c99 -pedantic -Wall test.c test.c: In function 'main': test.c:6:2: warning: unknown conversion type character 'h' in format [-Wformat] test.c:6:2: warning: too many arguments for format [-Wformat-extra-args] 

Ahora, lo que es extraño para mí es que se queja sobre la llamada snprintf en la línea 6, pero no sobre la llamada printf en la línea 4. ¿Me falta algo o la advertencia es incorrecta? Además, ¿existe quizás un equivalente mejor para la cadena de formato "%2hhX" ? (Estoy tratando de imprimir las variables char como valores hexadecimales).

Históricamente, MinGW ha estado en una situación un poco rara, especialmente en lo que respecta al soporte de C99. MinGW se basa principalmente en el tiempo de ejecución msvcrt.dll que se distribuye con Windows, y ese tiempo de ejecución no es compatible con C99.

Por lo tanto, con las versiones anteriores de MinGW, puede tener problemas en el modo C99 al usar especificadores de formato específicos de C99. También históricamente, GCC no realizó adaptaciones especiales para la falta de compatibilidad de msvcrt.dll para los especificadores de C99. Entonces, te -Wformat a situaciones en las que -Wformat no advertiría sobre un formato que no funcionaría.

Las cosas están mejorando en ambos lados: GCC tiene soporte específico para -Wformat cuando se usa con el tiempo de ejecución de MS, como:

  • -Wpedantic-ms-format para que GCC no se queje de "I32" y "I64" (aunque está documentado, todavía recibo una queja por no haber sido reconocido incluso en 4.7.0, tal vez sea completamente nuevo)
  • la opción ms_printf para __attribute__((__format__))

Por otro lado, MinGW ha proporcionado su propio snprintf() por un tiempo, ya que la variante de _snprintf() , _snprintf() , se comporta de manera muy diferente. Sin embargo, MinGW se basó durante mucho tiempo en el printf() en msvcrt.dll, por lo que los especificadores de formato C99 para printf() no funcionaron. En algún momento, MinGW comenzó a proporcionar su propia versión de printf() y amigos para que pueda obtener el soporte adecuado de C99 (y GNU?). Sin embargo, parece que para estar en el lado conservador, estos no reemplazaron las versiones de msvcrt.dll inicialmente. Tienen nombres como __mingw_printf() .

Parece que en algún punto entre 4.6.1 y 4.7.0, los encabezados de MinGW comenzaron a usar las versiones suministradas por MinGW como reemplazos para la función msvcrt.dll (al menos si has especificado C99).

Sin embargo, parece que con las versiones más recientes, GCC y MinGW aún no están sincronizados. Donde, como antes, GCC no advertiría sobre los especificadores que realmente no funcionarían en MinGW, no se quejaría sobre los activadores que lo harían.

Es posible que desee probar el siguiente fragmento de código para ver qué tan bien su versión de MinGW admite "hhX" :

 printf("%hhX\n", 0x11223344); __mingw_printf("%hhX\n", 0x11223344); 

No estoy seguro de qué sugerir para solucionar el problema con el que se está ejecutando: creo que puede aplicar un parche al encabezado stdio.h MinGW para que tenga un __attribute__((__format__ (gnu_printf, ...))) atributo en las funciones de printf (no están allí en la stdio.h más nueva de stdio.h , por lo que GCC usará su idea predeterminada de qué es el soporte de formato).

Además de la otra respuesta, aquí hay más información sobre las comprobaciones de formato de printf en GCC:

Cuando dice __attribute__((__format__ (FORMAT, ...))) , el valor de FORMAT puede ser (en lo que concierne a printf) uno de los siguientes: printf , gnu_printf , ms_printf .

ms_printf hace que GCC asum que la función toma una cadena de formato destinada a las funciones de la familia de impresoras CRT de Microsoft Visual Studio. Significa que GCC se quejará de z , hh y ll , pero pasará I64 sin previo aviso.

gnu_printf hace que GCC asum la implementación de GNU libc printf debajo (o tal vez solo una implementación de printf compatible con POSIX / C99, no estoy seguro). Por lo tanto, GCC se quejará de I64 y otras extensiones de Microsoft, pero aceptará z , hh y ll .

printf es un alias para ms_printf al comstackr para Windows, y un alias para gnu_printf contrario.

Tenga en cuenta que esta comprobación es completamente ortogonal a la implementación real de printf que se está utilizando. Esto es fácil de ver si escribes tu propia función similar a printf y pones __attribute__((__format__ (FORMAT, ...))) . GCC se quejará de diferentes cosas dependiendo de FORMAT , pero puedes hacer lo que quieras dentro. la función.

Implementaciones de printf disponibles que conozco:

  • MinGW ANSI STDIO (comstackr con -D__USE_MINGW_ANSI_STDIO=1 ) en las cadenas de herramientas MinGW.org y MinGW-w64. Cumple con el ms_printf (¿totalmente?) Y gnu_printf (parcialmente – no admite argumentos posicionales).
  • MSVCRT (comstackr sin -D__USE_MINGW_ANSI_STDIO=1 ). Cumple con ms_printf (duh …), el cumplimiento con gnu_printf es muy bajo y depende de la versión en tiempo de ejecución (las versiones anteriores no eran compatibles con ll , las nuevas sí; z y hh no están disponibles en ninguna versión hasta el momento; GCC no tiene ni gnu_printf estos desarrollos, sin embargo, y asume el peor de los casos, parece que msvcrt de la era VC 6.0.
  • gnulib. Cumple con ms_printf y gnu_printf completamente (o casi completamente).

El encabezado stdio.h en MinGW.org no usa el attribute format .

El encabezado stdio.h en MinGW-w64 usa el attribute format gnu_printf para la attribute format gnu_printf de MinGW ANSI STDIO, pero no usa nada para la implementación de MSVCRT. CORREGIDO: en las versiones más recientes de los encabezados MinGW-w64, stdio.h utilizará el attribute format ms_printf para la implementación de MSVCRT.

gnulib es plenamente consciente de la diferencia entre printf y gnu_printf , y seleccionará una u otra dependiendo de algunas macros complicadas (presumiblemente, acompañándolas con una implementación adecuada que respalde lo que el formato dice).

Piezas de software que se sabe (en este momento) que tienen problemas con las comprobaciones de formato GCC:

  • glib: utiliza el formato printf , pero la implementación es de gnulib; Hay un error sobresaliente para cambiarlo a gnu_printf
  • CPython: el código está lleno de formatos z , pero los binarios oficiales se crean contra MSVCRT; también usa el formato printf en sus cabeceras de extensión, aunque las extensiones a menudo también usan z