2012-05-20 15 views
10

Bien, me he encontrado con un extraño problema al compilar un archivo C con MinGW (GCC 4.6.2) en Windows 7. El archivo en cuestión contiene el siguiente código C:MinGW GCC: "Desconocido tipo de conversión carácter 'h'" (snprintf)

#include <stdio.h> 

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 compilació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 de la snprintf llamada en la línea 6, pero no la llamada printf en línea 4. ¿Me estoy perdiendo algo o la advertencia es incorrecta? Además, ¿hay quizás un mejor equivalente para el formato de cadena "%2hhX"? (Estoy intentando imprimir variables char como valores hexadecimales).

+0

Curiosamente, funciona bien con GCC 4.3.4: http://ideone.com/LAPP9. También lo intenté con 4.1.2 y también está bien. –

+0

Uso de MinGW GCC 4.6.1 Recibo advertencias en 'printf()' y 'snprintf()' - ¿Qué distro de MinGW está usando? Actualmente estoy usando la distribución TDM. –

+0

@MichaelBurr: Uh, ni siquiera me di cuenta de que había varias distribuciones de MinGW. Estoy utilizando el "estándar", supongo ([mingw.org] (http://www.mingw.org/), instalado con https://sourceforge.net/projects/mingw/files/Installer/mingw -get-inst /). Sin embargo, eso marcaría la diferencia? – Socob

Respuesta

17

Históricamente, MinGW ha estado en una situación extraña, especialmente en lo que respecta al soporte 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.

De modo que 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 hizo ningún ajuste especial para la falta de soporte de msvcrt.dll para los especificadores C99. De modo que entraría en situaciones en las que -Wformat no advertiría acerca de un formato que no funcionaría.

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

  • -Wpedantic-ms-format tanto GCC no se quejará de "I32" y "I64" (aunque es documentada, sigo teniendo una queja acerca de que sea reconocido incluso en 4.7.0 - tal vez es nuevo)
  • la opción ms_printf a __attribute__((__format__))

por Por otro lado, MinGW ha proporcionado su propio snprintf() por un tiempo, ya que la variante de MSVC, _snprintf(), se comporta de manera bastante 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 compatibilidad adecuada con C99 (y GNU?). Sin embargo, parece que para ser conservadores, 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 MinGW comenzaron a utilizar las versiones suministradas MinGW como reemplazos para la función msvcrt.dll (al menos si ha especificado C99).

Sin embargo, parece que con las versiones más nuevas, GCC y MinGW aún están un poco fuera de sincronización. Mientras que antes GCC no advertía acerca de los especificadores que en realidad no funcionarían en MinGW, no se queja de los spcifiers que lo harán.

es posible que desee probar el siguiente snipet de código para ver qué tan bien su versión de MinGW apoyo "hhX":

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

No estoy seguro de qué sugerir para solucionar el problema que se está ejecutando en - Creo que es posible que pueda parchear el encabezado MinGW stdio.h para que tenga un atributo __attribute__((__format__ (gnu_printf, ...))) en las funciones de impresión (no están en el nuevo stdio.h, por lo que GCC usará su idea predeterminada de lo que es el soporte de formato).

+0

Su fragmento de código no ofrece advertencias cuando lo compilo con las opciones de arriba e imprime '44' dos veces, como era de esperar; parece que solo 'snprintf' en particular se ve afectado por la advertencia en mi instalación. Sin embargo, el código que publiqué arriba imprime 'FA' dos veces, por lo que la función parece estar funcionando correctamente. Entonces, esencialmente, la advertencia es de hecho incorrecta y es solo una peculiaridad con MinGW y GCC? Si es así, eso es todo lo que realmente necesito saber, solo quería averiguar si me perdí algo en el código o si podía ignorar la advertencia. – Socob

+0

Gracias. Incluso en MinGW 4.9.1 '__mingw_printf' es el que funciona para mí sin advertencias. Lo envolverá en una macro para generalidad. – legends2k

3

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

Cuando dice __attribute__((__format__ (FORMAT, ...))), el valor de FORMAT puede ser (por lo que printf se refiere) una de las siguientes : printf, gnu_printf, ms_printf.

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

gnu_printf hace que GCC asuma la implementación GNU libc printf debajo (o tal vez solo una implementación de printf 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 compilar para Windows, y un alias para gnu_printf de lo contrario.

Tenga en cuenta que esta comprobación es completamente ortogonal a la implementación actual de printf que se utiliza. Esto es fácil de ver si escribe su propia función printf y pone __attribute__((__format__ (FORMAT, ...))) en ella - GCC se quejará de diferentes cosas dependiendo de FORMAT, pero puede hacer lo que quiera dentro de la función.

disponibles implementaciones printf, que yo sepa:

  • MinGW ANSI Stdio (compilar con -D__USE_MINGW_ANSI_STDIO=1) en MinGW.org y cadenas de herramientas MinGW-W64. Cumple con el formato ms_printf (¿completo?) Y gnu_printf (parcialmente - no admite argumentos posicionales).
  • MSVCRT (compilar 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 de tiempo de ejecución (las versiones anteriores no admitían ll, las nuevas lo hacen; z y hh no son compatibles con ninguna versión hasta ahora; GCC no tiene conocimiento de estos desarrollos, sin embargo, y asume el peor de los casos, msvcrt de la era VC 6.0, parece).
  • gnulib. Cumple con ms_printf y gnu_printf completamente (o casi completamente).

El encabezado stdio.h en MinGW.org no utiliza attribute format.

El encabezado stdio.h en MinGW-w64 usa attribute format gnu_printf para la implementación de STDIO ANSI MinGW, 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á 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, lo acompaña con una implementación adecuada que admite lo que dice el formato).

piezas de software que son conocidos (por el momento) a tener problemas con formato GCC cheques:

  • simplistas - utiliza printf formato, pero la implementación es de gnulib; hay un error pendiente para cambiarlo a gnu_printf
  • CPython - el código está lleno de formatos z, pero los binarios oficiales están construidos contra MSVCRT; sino que también utiliza printf formato en sus cabeceras de extensión, a pesar de que a menudo usan extensiones z así
Cuestiones relacionadas