Okay, I've run into a strange issue compiling a C file with MinGW (GCC 4.6.2) on Windows 7. The file in question contains the following C code:
#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;
}
The compilation turns out like this:
$ 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]
Now, what's strange to me is that it complains about the snprintf
call on line 6, but not the printf
call on line 4. Am I missing something or is the warning just incorrect? Also, is there perhaps a better equivalent for the format string "%2hhX"
? (I'm trying to print char variables as hexadecimal values.)
Historically, MinGW has been in a bit of an odd situation, especially as far as C99 support goes. MinGW relies mostly on the msvcrt.dll runtime that's distributed with Windows, and that runtime doesn't support C99.
So with older versions of MinGW, you can run into problems in C99 mode when using C99-specific format specifiers. Also historically, GCC didn't make any special accommodations for msvcrt.dll's lack of support for C99 specifiers. So you'd get into situations where -Wformat
wouldn't warn about a format that wouldn't work.
Things are improving on both sides - GCC has specific support for -Wformat when used with the MS runtime, such as:
-Wpedantic-ms-format
so that GCC won't complain about "I32"
and "I64"
(even though it's documented, I still get a complaint about it being unrecognized even in 4.7.0 - maybe it's brand new)ms_printf
option to __attribute__((__format__))
On the other side, MinGW has provided its own snprintf()
for a while, since MSVC's variant, _snprintf()
, behaves quite differently. However, MinGW relied for a long while on the printf()
in msvcrt.dll, so C99 format specifiers for printf()
didn't work. At some point MinGW started providing it's own version of printf()
and friends so that you could get proper C99 (and GNU?) support. However, it seems that to be on the conservative side, these didn't replace the msvcrt.dll versions initially. They have names like __mingw_printf()
.
It looks like at some point between 4.6.1 and 4.7.0, the MinGW headers started using the MinGW supplied versions as replacements for the msvcrt.dll function (at least if you've specifed C99).
However, it seems that with the newer versions, GCC and MinGW are still a little out of sync. Where as before GCC would not warn about specifiers that wouldn't actually work on MinGW, not it complains about spcifiers that will.
You may want to try the following snipet of code to see how well your version of MinGW support "hhX"
:
printf("%hhX\n", 0x11223344);
__mingw_printf("%hhX\n", 0x11223344);
I'm not sure what to suggest to fix the problem you're running into - I think that you may be able to patch the MinGW stdio.h
header so that it has a __attribute__((__format__ (gnu_printf, ...)))
attribute on the printf functions (they're not there in the newer stdio.h
, so GCC will use it's default idea of what the format support is).
In addition to the other answer, here is some more info on printf format checks in GCC:
When you say __attribute__((__format__ (FORMAT, ...)))
, the value of FORMAT
can be (as far as printf is concerned) one of the following: printf
, gnu_printf
, ms_printf
.
ms_printf
makes GCC assume that function takes a format string intended for Microsoft Visual Studio CRT printf family functions. It means that GCC will complain about z
, hh
and ll
, but will pass I64
without warning.
gnu_printf
makes GCC assume GNU libc printf implementation underneath (or maybe just a POSIX/C99-compliant printf implementation, i'm not sure). Therefore GCC will complain about I64
and other Microsoft extensions, but will accept z
, hh
and ll
.
printf
is an alias for ms_printf
when compiling for Windows, and an alias for gnu_printf
otherwise.
Note that this check is completely orthogonal to the actual printf implementation being used. This is easy to see if you write your own printf-like function and put __attribute__((__format__ (FORMAT, ...)))
on it - GCC will complain about different things depending on FORMAT
, but you can do whatever you want inside the function.
Available printf implementations that i know of:
-D__USE_MINGW_ANSI_STDIO=1
) in MinGW.org and MinGW-w64 toolchains. Complies with ms_printf
(fully?) and gnu_printf
format (partially - does not support positional arguments).-D__USE_MINGW_ANSI_STDIO=1
). Complies with ms_printf
(duh...), compliance with gnu_printf
is very low and depends on runtime version (old versions did not support ll
, new ones do; z
and hh
are not supported in any version so far; GCC is blissfully unaware of these developments though, and assumes the worst case, msvcrt from VC 6.0 era, it seems).ms_printf
and gnu_printf
completely (or near-completely).The stdio.h
header in MinGW.org does not use attribute format
.
The stdio.h
header in MinGW-w64 uses attribute format gnu_printf
for MinGW ANSI STDIO implementation, but does not use anything for MSVCRT implementation. FIXED: In newer versions of MinGW-w64 headers stdio.h
will use attribute format ms_printf
for MSVCRT implementation.
gnulib is fully aware of the difference between printf
and gnu_printf
, and will pick one or the other depending on some complicated macros (presumably, accompanying it with a proper implementation that supports what the format says it does).
Pieces of software that are known (at the moment) to have problems with GCC format checks:
printf
format, but implementation is from gnulib; there's an outstanding bug for changing it to gnu_printf
z
formats, but official binaries are built against MSVCRT; it also uses printf
format in its extension headers, even though extensions often use z
as wellIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With