I have a custom implementation of printf which I use in my school projects.
Wishing to have the same warnings that printf, I use __attribute__((format (printf ...)). That works fine, but using -Wall -Wextra -Werror -std=c11 -pedantic-errors, I get the error ISO C does not support %n$ operand number formats on gcc 7.2.0. (My implementation support that format).
On clang it does not shout a warning if I use std=c11, but does with std=c99.
Is that format part of the c11 specification (as clang behavior let me think) or only the Singe Unix Specification (that is what my man page says) ? If so, how can I use it ? In this page, I cannot find it as an option to std.
I would rather not disable any warnings, since they are quite useful and catches a lot. But is there a way to allow that format in format strings, or specify only for my function ?
Thanks a lot.
P.S: I use a custom implemenation of printf because in my school projects we must only use specific allowed functions (basically, system api like malloc, free, read etc), or the one we have done ourselves.
EDIT : for reference, the format %n$, with n being string of digit for a non-zero number, allow to access the argument at that index (starting at one) in the list of variable arguments given to printf. (And can be used for the conversion itself or for the precision or field widht with the * operand).
You could use gnu_printf instead of printf in the attribute to model a behavior that comes closer to POSIX' printf.
Related
In my code I use the following line to print a char readbuffer[1]; array (char array with size 1):
printf(readbuffer);
This compiles and works without an issue on my pc (Arch Linux, gcc version 7.3.1+20180406-1). However, when I submitted my assignment containing this code to my instructor, he in fact got a compiler warning compiling my code:
shell.c:89:20: warning: format not a string literal and no format arguments [-Wformat-security]
printf(readbuffer);
He is using the gcc/clang version from 16.04 LTS release. We both used the same compiler flags.
Why is this? Is this suddenly not an issue anymore in the new gcc version? If so, why not?
Just a note: I don't want to know how to solve this issue, but just want to know why the warning is inconsistent over gcc versions.
I don't want to know how to solve this issue ...
Yes, you really do!
Unless your char[1] variable always contains \0, what you are doing is unsafe. And. if it does contain that, what you are doing is nothing :-)
The correct way to do what you're trying to do is, assuming a need for printf:
printf("%.1s", readbuffer);
This will ensure you don't try to read past that single character. Of course, if you know that there will always be a character, just use:
putchar(*readbuffer);
In terms as to why different gcc versions report differently, that can be put down to simple improvement over time. It's the same reason why, for example, gcc will complain about mismatch between number of format specifiers and number of arguments in something like:
printf ("%s %d\n", "hello");
whereas some other implementations may not.
Specifically, while the standard mandates what must be reported as a diagnostic, it does not otherwise limit what implementations may report as a diagnostic beyond that.
Later versions of a compiler may add or remove these optional diagnostics, or change how they decide to report on them.
This is not caused by a difference in GCC versions. Rather, Ubuntu has modified GCC to enable -Wformat -Wformat-security by default. If you pass those options on Arch Linux, you should see the same behaviour there.
I am trying to get rid of a bogus warning in my program. Under windows 64 (under linux there is no such warning) this statement:
printf("%llu",UINT64_MAX);
generates the following warning:
warning: unknown conversion type character 'l' in format [-Wformat]|
The output appears to be correct and the warning should not be there. The most relevant gcc related post I can find is this bug report back from 2008 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37768
If I understand correctly according to that, this warning stems from the fact that under the hood gcc calls MSVC printf which is not C99 compliant and can't understand the unsigned long long format in printf. From the same page the suggested solution is to use something called gnu_printf. I tried to google that but I did not find a header to include.
So the question is how should this be handled in order to be portable? I just want to get rid of the warning in a correct and non-sloppy way.
As I'm guessing you probably already know, from http://comments.gmane.org/gmane.comp.gnu.mingw.w64.general/4670 (note: dead link; see the Internet Archive's copy),
the issue is that formatter-width specifier %ll isn't supported for
all msvcrt-DLL versions, therefore gcc warns about its use. The
variant for specifying 64-bit integer-scalar-width in formatter for
msvcrt in a backward-compatible way is by using %I64.
With gcc 6.2.1 you can use -fno-builtin option.
That option makes gcc think that printf isn't built-in (which is the case, after all), and the warning goes away, printf is treated just like any function you'd have written.
Careful though, as no format check is performed anymore in that case, and there are other side-effects as there are other built-ins covered by that option.
A better alternative is not to use Microsoft version of printf at all by setting the macro __USE_MINGW_ANSI_STDIO with:
gcc -D__USE_MINGW_ANSI_STDIO=1 ...
(ref: C program shows %zu after conversion to Windows). It allows to use %zu specifier, that Microsoft printf doesn't support and you keep formatting warnings.
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)
the 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:
MinGW ANSI STDIO (compile with -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).
MSVCRT (compile without -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).
gnulib. Complies with 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:
glib - uses printf format, but implementation is from gnulib; there's an outstanding bug for changing it to gnu_printf
CPython - the code is full of 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 well
Gcc nicely provides -Wformat to help with finding printf related bugs. Is there any way to get the same behavior in MSVC? Specifically I'd like the compiler to do some level of type checking on the arguments. I explicitely don't want to use C++'s iostream library for various reasons. (and I also don't want to use boost format).
To quote the source above, -WFormat basically provides the following capabilities
Check calls to printf and scanf, etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense.
The closest I can find for Microsoft so far is this warning which relates to using %d for 64 vs 32 bit builds.
I believe this is not a supported feature in Visual Studio (I'll try to find a citation for this). The closest I am aware of is to use the _Printf_format_string_ SAL annotation.
Unfortunately there is no way to generate such warning at compile time, but the VC++ code analysis tools will generate warning messages for printf-like functions with mismatching parameters.
See the /analyze option in VC++ and http://msdn.microsoft.com/en-us/library/vstudio/ms173498.aspx for more details.
As a side note, people have been complaining about this, so maybe Microsoft will do something in the future:
https://connect.microsoft.com/VisualStudio/feedback/details/799869/detection-of-format-string-errors-should-be-part-of-the-regular-c-compile-instead-of-analyze
Specifically I'd like the compiler to do some level of type checking
on the arguments.
The compiler loves to do type checking by default in C++ code. Unfortunately you're trying to use C facilities that don't offer that capability.
Just use IO streams and the compiler will do more than issue a warning when the types mismatch: It will issue an error and fail to compile your code completely!
Visual Studio has a size and distance specification chart that says I can do something like this, using the h prefix to specify single byte character string regardless of printf or wprintf:
int main()
{
char test[]= "abc";
_tprintf(_T("%hs\n"),test);
}
But if I compile the same thing with mingw gcc and -Wall I get the following warning:
warning: format '%hs' expects type 'short int *', but argument 2 has type 'char *'
Is what I'm doing an acceptable way to specify a single byte character string in mingw?
Thanks
Edit-This question has been answered below in fair detail. The short answer is yes, it's an acceptable way to specify a single byte character string in mingw and the warning can be ignored.
You'll also note that the Visual Studio docs say:
Note: The h and l prefixes are Microsoft extensions when used with data of type char.
I think that this would be more accurate to say, "when used with the 'c' or 's' format specifiers (upper or lower case)".
Realize that the GCC compiler really doesn't know about how Microsoft's runtime deals with printf() format strings and the warning GCC is giving is tailored to the runtimes that it more normally uses. When building with MinGW, a Microsoft C runtime that's provided with Windows, msvcrt.dll, is used (though MinGW does provide replacements or wrappers for some library functions). So there is sometimes confusing between what the compiler thinks is OK for a printf() format string and the arguments provided to printf() and what the runtime will actually do.
For example, until recently, using ll on an integer conversion to format a 64-bit int type wouldn't work correctly in MinGW. I'm not sure if the 'fix' for that occurred in the MinGW support functions or if msvcrt.dll was updated to support the ll modifier (I suspect that msvcrt.dll was updated - I'll have to check...).
Anyway, what this boils down to is that regardless of the warnings that GCC gives about the printf() format string, in all likelihood you'll need to use the MSVC docs for format strings, since its an MS runtime that MinGW will be using. If you don't like the warngin being generated you might want to consider adding the -Wno-format option to your MinGW build script.