2012-05-20 7 views
10

Ok, ho avuto uno strano problema compilando un file C con MinGW (GCC 4.6.2) su Windows 7. Il file in questione contiene il seguente codice C:GCC MinGW: "Tipo di conversione sconosciuto carattere '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 compilazione risulta in questo modo:

$ 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] 

Ora, ciò che è strano per me è che si lamenta della snprintf chiamata sulla linea 6, ma non il printf chiamata su linea 4. Mi manca qualcosa o l'avviso è errato? Inoltre, esiste forse un equivalente migliore per la stringa di formato "%2hhX"? (Sto provando a stampare le variabili del char come valori esadecimali.)

+0

interessante, funziona benissimo con GCC 4.3.4: http://ideone.com/LAPP9. Ho anche provato con 4.1.2, e va bene anche su quello. –

+0

Uso di GG MinGW 4.6.1 Ricevo avvisi sia su 'printf()' che su 'snprintf()' - quale distro MinGW stai usando? Attualmente sto usando la distribuzione TDM. –

+0

@ Michael Burr: Uh, non mi ero nemmeno reso conto che c'erano diverse distribuzioni MinGW. Sto usando quello "standard", immagino ([mingw.org] (http://www.mingw.org/), installato con https://sourceforge.net/projects/mingw/files/Installer/mingw -get-inst /). Questo farebbe la differenza, però? – Socob

risposta

17

Storicamente, MinGW è stato in una situazione un po 'strana, specialmente per quanto riguarda il supporto C99. MinGW si basa principalmente sul runtime msvcrt.dll distribuito con Windows e che runtime non supporta C99.

Quindi con le versioni precedenti di MinGW, è possibile incorrere in problemi in modalità C99 quando si utilizzano specificatori di formato specifici per C99. Inoltre, storicamente, GCC non ha apportato adattamenti speciali alla mancanza di supporto di msvcrt.dll per gli specificatori C99. Quindi ti trovavi in ​​situazioni in cui -Wformat non avvisava di un formato che non avrebbe funzionato.

Le cose stanno migliorando su entrambi i lati - GCC ha il supporto specifico per -Wformat quando viene utilizzato con il runtime MS, come ad esempio:

  • -Wpedantic-ms-format in modo che GCC non lamentarsi "I32" e "I64" (anche se è documentato, ho ancora una denuncia sul fatto che sia riconosciuto anche in 4.7.0 - forse è nuovo di zecca)
  • l'opzione ms_printf al __attribute__((__format__))

Sul Dall'altra parte, MinGW ha fornito il proprio snprintf() per un po ', poiché la variante di MSVC, _snprintf(), si comporta in modo molto diverso. Tuttavia, MinGW si affidava a lungo allo printf() in msvcrt.dll, quindi gli specificatori di formato C99 per printf() non funzionavano. A un certo punto, MinGW ha iniziato a fornire la propria versione di printf() e gli amici in modo da ottenere il supporto C99 (e GNU?) Corretto. Tuttavia, sembra che dal lato conservatore, questi non hanno sostituito inizialmente le versioni di msvcrt.dll. Hanno nomi come __mingw_printf().

Sembra che ad un certo punto tra 4.6.1 e 4.7.0, le intestazioni MinGW inizino a utilizzare le versioni fornite da MinGW come sostituti della funzione msvcrt.dll (almeno se è stato specificato C99).

Tuttavia, sembra che con le versioni più recenti, GCC e MinGW siano ancora un po 'fuori sincrono. Dove prima GCC non avrebbe avvertito gli specificatori che non avrebbero funzionato su MinGW, non si lamenta degli spcifiers che lo faranno.

si consiglia di provare il seguente snipet di codice per vedere come la versione di sostegno MinGW "hhX":

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

Non sono sicuro di cosa suggerire per risolvere il problema si sta eseguendo in - Penso che potresti essere in grado di patchare l'intestazione MinGW stdio.h in modo che abbia un attributo __attribute__((__format__ (gnu_printf, ...))) sulle funzioni printf (non ci sono nel nuovo stdio.h, quindi GCC userà l'idea predefinita di quale sia il supporto del formato).

+0

Lo snippet di codice non dà avvisi quando lo compilo con le opzioni dall'alto e stampa '44' due volte, come ci si aspetterebbe; sembra che solo "snprintf" in particolare sia influenzato dall'avvertimento sulla mia installazione. Tuttavia, il codice che ho postato sopra stampa 'FA' due volte, quindi la funzione sembra funzionare correttamente. Quindi, in sostanza, l'avviso è davvero errato ed è solo una stranezza con MinGW e GCC? Se è così, è tutto ciò che ho davvero bisogno di sapere - volevo solo capire se mi mancava qualcosa nel codice o se potevo ignorare l'avvertimento. – Socob

+0

Grazie. Anche su MinGW 4.9.1 '__mingw_printf' è quello che funziona per me senza avvertimenti. Lo avvolgerà in una macro per generalità. – legends2k

3

Oltre alla altra risposta, ecco alcune ulteriori informazioni sui controlli di formato printf in GCC:

Quando si dice __attribute__((__format__ (FORMAT, ...))), il valore di FORMAT può essere (per quanto printf è interessato) una delle seguenti : printf, gnu_printf, ms_printf.

ms_printf fa assumere a GCC che la funzione utilizzi una stringa di formato destinata alle funzioni della famiglia printf CRT di Microsoft Visual Studio. Ciò significa che GCC si lamenterà di z, hh e ll, ma passerà senza preavviso I64.

gnu_printf fa in modo che GCC assuma un'implementazione di GNU libc printf al di sotto (o forse solo un'implementazione printf conforme a POSIX/C99, non sono sicuro). Pertanto GCC si lamenterà di I64 e altre estensioni Microsoft, ma accetterà z, hh e ll.

printf è un alias per ms_printf durante la compilazione per Windows e un alias per gnu_printf in caso contrario.

Si noti che questo controllo è completamente ortogonale all'implementazione effettiva di printf utilizzata. Questo è facile da vedere se scrivi la tua funzione printf-like e metti __attribute__((__format__ (FORMAT, ...))) su di esso - GCC si lamenterà di cose diverse a seconda di FORMAT, ma puoi fare tutto ciò che vuoi all'interno della funzione.

implementazioni printf disponibili, che io sappia:

  • MinGW ANSI STDIO (compilare con -D__USE_MINGW_ANSI_STDIO=1) in MinGW.org e toolchain MinGW-W64. Conforme al formato ms_printf (completamente?) E gnu_printf (parzialmente - non supporta gli argomenti posizionali).
  • MSVCRT (compilare senza -D__USE_MINGW_ANSI_STDIO=1). Conforme allo standard ms_printf (duh ...), la conformità con gnu_printf è molto bassa e dipende dalla versione di runtime (le vecchie versioni non supportavano ll, le nuove versioni z e hh non sono supportate in nessuna versione fino ad ora; questi sviluppi però, e presume il caso peggiore, msvcrt dell'era VC 6.0, sembra).
  • gnulib. Conforme a ms_printf e gnu_printf completamente (o quasi completamente).

L'intestazione stdio.h in MinGW.org non utilizza attribute format.

L'intestazione stdio.h in MinGW-w64 utilizza attribute format gnu_printf per l'implementazione ANSI STDIO MinGW, ma non utilizza nulla per l'implementazione MSVCRT. FISSO: Nelle versioni più recenti delle intestazioni MinGW-w64 stdio.h utilizza attribute format ms_printf per l'implementazione MSVCRT.

gnulib è pienamente consapevole della differenza tra printf e gnu_printf e sceglierà l'uno o l'altro a seconda di alcune macro complicate (presumibilmente, accompagnandolo con un'implementazione corretta che supporta ciò che il formato dice di fare).

pezzi di software che sono noti (al momento) ad avere problemi con i controlli del formato GCC:

  • glib - utilizza il formato printf, ma l'attuazione è da gnulib; c'è un bug in sospeso per cambiarlo in gnu_printf
  • CPython - il codice è pieno di formati z, ma i binari ufficiali sono costruiti contro MSVCRT; utilizza anche printf formato nelle sue intestazioni di estensione, anche se le estensioni spesso usano z così