In this series of posts I'm discussing how compiling with multiple compilers and warnings enabled can help to catch errors. In the last post I discussed how to turn return type warnings into errors. In this post, I'm going to look at another warning that I often promote to an errors.
Background
As discussed in my previous posts, you can turn specific warnings into error using the -Werror=warning-name
command line flag on GCC and Clang. In some cases you can also annotate code in order enable checking it for a warning type.
Format
The format warning lets you know when you have used the incorrect format specifier for the printf
family of functions. For example:
12long
i
=
4294967295
;
printf(
"%d\n"
, i);
In the above example, using %d
instead of %ld
may print the value -1
instead of the expected value 4294967295
. For a simple cause of a log statement, the incorrect value might not matter, however, if you are serializing a value to a file it may cause issues for your users.
In another example:
12long
i
=
1
;
printf(
"%s\n"
, i);
You are attempting to print a number using the string format specifier. This specifier expects a null terminated string and as a result it will cause undefined behaviour which could create security and stability issues.
Catching the error
Clang
When compiling with Clang this is a warning by default when using no compiler flags. To promote the warning to an error you can use -Werror=format
. Below is an example of the warning from Clang:
123456789<source>:
23
:
18
: warning:
format
specifies
type
'int'
but the argument has
type
'int64_t'
(aka
'long'
) [
-
Wformat]
printf(
"%d\n"
, i);
~~ ^
%
ld
<source>:
24
:
18
: warning:
format
specifies
type
'char *'
but the argument has
type
'int64_t'
(aka
'long'
) [
-
Wformat]
printf(
"%s\n"
, i);
~~ ^
%
ld
2
warnings generated.
GCC
For GCC the warning is not enabled by default. It is included as part of the -Wall
flag or can be enabled individually using -Wformat
. To promote it to an error you can use -Werror=format
.
Note: From v8.0 of GCC you will receive a warning with no additional compiler flags.
MSVC
With MSVC the warning for standard printf functions is enabled by default.
Attributing your own functions
It can sometimes be the case that you want to make your own function which takes a format specifier (e.g. a logging function):
123456789void my_logger(
int
level, const char
*
format
, ...)
{
if
(level >
=
LOGGING_LEVEL) {
va_list(args);
va_start(args,
format
);
vfprintf(stderr,
format
, args);
va_end(args);
}
}
Which you can call as:
1my_logger(WARN,
"%s\n"
, i);
In this case the normal format warning won't appear in you code to warn you of errors. If using GCC and Clang, you can add a non-standard function attribute to tell the compiler that a function takes a format argument.
1void my_logger(
int
level, const char
*
format
, ...) __attribute__ ((
format
(printf,
2
,
3
)));
After adding this you can will receive the following warning warning:
1234<source>:
26
:
27
: warning:
format
specifies
type
'char *'
but the argument has
type
'int64_t'
(aka
'long'
) [
-
Wformat]
my_logger(WARN,
"%s\n"
, i);
~~ ^
%
ld
Note: This is not available on MSVC, so the addition of this attribute should be hidden behind a conditional compiler flag.
Comparing the errors
To view the errors from each compiler you can use godbolt to compare the output.
No comments:
Post a Comment