2012-05-20 14 views
10

Okay, natknąłem się na dziwny problem podczas kompilowania pliku C za pomocą MinGW (GCC 4.6.2) w systemie Windows 7. Ten plik zawiera następujący kod C:MinGW GCC: "Nieznany typ znaku konwersji" 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; 
} 

Kompilacja okazuje się tak:

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

teraz, co jest dla mnie dziwne jest to, że skarży się na wezwanie snprintf na linii 6, ale nie rozmowy printf on-line 4. Czy brakuje mi czegoś lub czy ostrzeżenie jest po prostu nieprawidłowe? Być może istnieje lepszy odpowiednik dla ciągu formatu "%2hhX"? (Próbuję drukować zmienne znaków w postaci wartości szesnastkowych.)

+0

Co ciekawe, to działa dobrze z GCC 4.3.4: http://ideone.com/LAPP9. Próbowałem również z 4.1.2, i to też w porządku. –

+0

Używanie MinGW GCC 4.6.1 Otrzymuję ostrzeżenia zarówno na 'printf()' jak i 'snprintf()' - jakiej używacie dystrybucji MinGW? Obecnie używam dystrybucji TDM. –

+0

@MichaelBurr: Uh, nawet nie zdawałem sobie sprawy, że istnieje kilka dystrybucji MinGW. Używam "standardowego", jak sądzę ([mingw.org] (http://www.mingw.org/), zainstalowanego z https://sourceforge.net/projects/mingw/files/Installer/mingw -get-inst /). Czy to jednak miało znaczenie? – Socob

Odpowiedz

17

Historycznie MinGW był w nieco dziwnej sytuacji, szczególnie jeśli chodzi o wsparcie dla C99. MinGW opiera się głównie na środowisku wykonawczym msvcrt.dll, które jest rozprowadzane w systemie Windows, i że środowisko wykonawcze nie obsługuje C99.

Tak więc w starszych wersjach MinGW można napotkać problemy w trybie C99 podczas korzystania ze specyfikatorów formatu C99. Również historycznie, GCC nie robi żadnych specjalnych noclegów dla msvcrt.dll na brak wsparcia dla specyfikatorów C99. A więc dostaniesz się do sytuacji, w których -Wformat nie ostrzegłby o formacie, który nie działał.

Rzeczy poprawy po obu stronach - GCC ma szczególne wsparcie -Wformat gdy używany ze środowiskiem wykonawczym MS, takich jak:

  • -Wpedantic-ms-format tak, że GCC nie będzie narzekać "I32" i "I64" (nawet jeśli jest to udokumentowane, wciąż otrzymuję skargi o to czy jest nierozpoznane nawet w 4.7.0 - może to nowiutki)
  • opcja ms_printf do __attribute__((__format__))

na z drugiej strony, MinGW przez jakiś czas udostępniał swój własny snprintf(), ponieważ wariant MSVC, _snprintf(), zachowuje się zupełnie inaczej. Jednak MinGW przez długi czas polegał na printf() w msvcrt.dll, więc specyfikatory formatu C99 dla printf() nie działały. W pewnym momencie MinGW zaczął dostarczać własną wersję printf() i znajomych, aby uzyskać właściwą obsługę C99 (i GNU?). Wydaje się jednak, że po stronie konserwatywnej nie zastąpiły one początkowo wersji msvcrt.dll. Mają nazwy takie jak __mingw_printf().

Wygląda na to, że w pewnym momencie między 4.6.1 a 4.7.0 nagłówki MinGW zaczęły używać dostarczonych wersji MinGW jako zamienniki dla funkcji msvcrt.dll (przynajmniej jeśli podałeś C99).

Wygląda jednak na to, że w nowszych wersjach GCC i MinGW wciąż nie są zsynchronizowane. Gdzie tak jak wcześniej GCC nie ostrzegałby o specyfikacjach, które nie działałyby na MinGW, nie narzekałby na to, że spiskowy to zrobi.

Możesz wypróbować następujące snipet kodu, aby zobaczyć, jak dobrze wersję MinGW wsparcia "hhX":

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

Nie jestem pewien, co sugeruje, aby rozwiązać problem używasz do - Myślę, że możesz być w stanie załączyć nagłówek MinGW stdio.h tak, aby miał atrybut __attribute__((__format__ (gnu_printf, ...))) dla funkcji printf (nie ma ich w nowszym stdio.h, więc GCC użyje domyślnego pomysłu na obsługę formatów).

+0

Twój fragment kodu nie daje żadnych ostrzeżeń, gdy skompiluję go z opcjami powyżej i wypisze '44' dwa razy, jak można się było spodziewać; Wygląda na to, że ostrzeżenie na mojej instalacji dotyczy tylko 'snprintf'. Mimo to, napisany powyżej kod wypisuje 'FA' dwa razy, więc funkcja wydaje się działać poprawnie. Więc, zasadniczo, ostrzeżenie jest rzeczywiście niepoprawne i jest tylko dziwactwem z MinGW i GCC? Jeśli tak, to wszystko, co naprawdę muszę wiedzieć - chciałem tylko dowiedzieć się, czy coś przeoczyłem w kodzie lub czy mogę zignorować ostrzeżenie. – Socob

+0

Dzięki. Nawet na MinGW 4.9.1 '__mingw_printf' jest tym, który działa dla mnie bez ostrzeżeń. Otoczy je w makrze na ogólność. – legends2k

3

W uzupełnieniu do innych odpowiedzi, tutaj jest trochę więcej informacji na temat kontroli formatu printf w GCC:

Kiedy mówisz __attribute__((__format__ (FORMAT, ...))) wartość FORMAT może być (w miarę printf jest zaniepokojony) jeden z następujących : printf, gnu_printf, ms_printf.

powoduje, że GCC zakłada, że ​​funkcja przyjmuje ciąg formatujący przeznaczony dla rodziny funkcji Printflex CRT printf rodziny. Oznacza to, że GCC będzie narzekać na: z, hh i ll, ale przekaże I64 bez ostrzeżenia.

gnu_printf sprawia, że ​​GCC zakłada implementację GNU libc printf pod spodem (a może tylko implementację printf zgodną z POSIX/C99, nie jestem pewien). Dlatego GCC będzie skarżyć się na I64 i inne rozszerzenia Microsoft, ale zaakceptuje z, hh i ll.

printf to alias dla ms_printf podczas kompilacji dla systemu Windows i alias dla gnu_printf w przeciwnym razie.

Należy zauważyć, że to sprawdzenie jest całkowicie ortogonalne w stosunku do faktycznej implementacji printf. Łatwo to zauważyć, jeśli napiszesz własną funkcję podobną do printf i umieścisz na niej __attribute__((__format__ (FORMAT, ...))) - GCC będzie narzekać na różne rzeczy w zależności od FORMAT, ale możesz robić, co chcesz w tej funkcji.

Dostępne implementacje printf które znam:

  • MinGW ANSI stdio (kompilacji z -D__USE_MINGW_ANSI_STDIO=1) w MinGW.org i MinGW-W64 toolchains. Zgodny z formatem ms_printf (w pełni?) I gnu_printf (częściowo - nie obsługuje argumentów pozycyjnych).
  • MSVCRT (kompilacja bez -D__USE_MINGW_ANSI_STDIO=1). Zgodny z ms_printf (duh ...), zgodność z gnu_printf jest bardzo niska i zależy od wersji środowiska wykonawczego (stare wersje nie wspierały ll, nowe mają, z i hh nie są jeszcze obsługiwane w żadnej wersji; GCC jest błogo nieświadome jednak te zmiany i zakłada, że ​​najgorszy przypadek, msvcrt z VC 6.0, wydaje się).
  • gnulib. Zgodny z ms_printf i gnu_printf całkowicie (lub prawie całkowicie).

Nagłówek stdio.h w MinGW.org nie używa attribute format.

Nagłówek w MinGW-w64 używa attribute format gnu_printf dla implementacji MinGW ANSI STDIO, ale nie używa niczego do implementacji MSVCRT. NAPRAWIONE: W nowszych wersjach nagłówków MinGW-w64 stdio.h użyje attribute format ms_printf dla implementacji MSVCRT.

gnulib jest w pełni świadomy różnicy między printf a gnu_printf i wybierze jeden lub drugi w zależności od skomplikowanych makr (prawdopodobnie towarzyszy mu odpowiednia implementacja, która obsługuje to, co format mówi).

elementy oprogramowania, które są znane (w tej chwili) mają problemy z kontroli formatu GCC:

  • Glib - używa formatu printf, ale realizacja jest z gnulib; jest niecodzienny błąd związany z jego zmienianiem na gnu_printf
  • CPython - kod jest pełen formatów z, ale oficjalne pliki binarne są budowane przeciwko MSVCRT; to również wykorzystuje format w swoich nagłówków rozszerzających printf, choć często używają rozszerzenia z także