Sometime in the gcc 4.5 era, a warning for the following expression was introduced and subsequently fixed apparently the development of the 10.x versions, i.e. newer compilers do no longer produce the warning.
The expression producing the warning immediately precedes the final return from main and reads as follows:
$ gcc -Wsign-conversion -o /tmp/banana /tmp/banana.c
/tmp/banana.c: In function ´main´:
/tmp/banana.c:40:5: warning: conversion to ´long unsigned int´ from ´int´ may change the sign of the result [-Wsign-conversion]
40 | : ((a_default <= a_max)
| ^
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
/* generic parsing function to make sure we retrieve values the
* compiler cannot make assumptions about without invoking UB
* (hopefully) */
static long
parse_long(const char *str)
{
char *endptr = NULL;
long val = strtol(str, &endptr, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
|| (errno != 0 && val == 0)) {
perror("strtol");
exit(EXIT_FAILURE);
}
if (endptr == str) {
fputs("No digits were foundn", stderr);
exit(EXIT_FAILURE);
}
return val;
}
int main(int argc, char **argv)
{
if (argc < 2) return 1;
errno = 0;
int val = (int)parse_long(argv[1]);
size_t a_default = 1024;
if (argc > 2)
a_default = (size_t)parse_long(argv[2]);
static const size_t a_max = 4096;
/* the above only serves to make sure the compiler cannot assume
* much about the values of a_default and val */
size_t a = (val > 0)
? (size_t)val
: ((a_default <= a_max)
? a_default : a_max);
return a > 0;
}
I now want to get rid of this warning, though incorrect, with as little fuss as possible. One none-obvious way I already found is changing val
to long
type.
While the obvious solution is to upgrade compilers, versions 8 and 9 of gcc are still very common production compilers and I’d really like to build without warnings on RHEL 8.x and Debian oldstable and Ubuntu 20.04. Any alternatives to prevent the warning or perhaps tell me how that part of the program is indeed doing something wrong is appreciated.
2
Answers
Looks like the warning message is being a bit deceiving and sort of appears to be a false positive. The warning believes
(size_t)val
is signed as if to ignore the cast. If you really don’t want that warning, using an actual variable in place of that cast will work:And also, as Retired Ninja pointed out,
parse_long
isn’t returning anything:First of all, I’d be mostly concerned with the warning regarding
parse_long
in any gcc version:That’s an actual bug.
As for the warning in your question, I assume you are using
-Wconversion
. This was never a reliable one in any gcc version and it is prone to "false positives". I can reproduce the problem up to version 11. The solution is to stop using-Wconversion
in the release build (it can sometimes be handy during debug builds).If we take a closer look on the expression to prove that this is a bug:
(val > 0)
is evaluated first, both parameters areint
, no implicit promotions.(size_t)val
explicitly converts tosize_t
. Ifval
was negative you’d lose information here but it literally can’t be negative.?:
,a_default <= a_max
have both parameters assize_t
.a_default : a_max
are both of typesize_t
so no conversion takes place.?:
aresize_t
and no implicit conversions take place.Furthermore, up to gcc 10 it says
Which is wrong since no such conversion takes place. And then between gcc 10 and 11 it says
Err… This warning along with the rest of
-Wconversion
is clearly not to be trusted.