comparing unsigned vs signed does not make a warning (using const)

Viewed 363

Simple: If I test a signed vs an unsigned variable in GCC, when compiling -Wall I will get a warning.

Using this code:

#include <stdio.h>

int main(int argc, char* argv[])
{
    /* const */ unsigned int i = 0;
    if (i != argc)
        return 1;
    return 0;
}

I get this warning:

<source>: In function 'int main(int, char**)':
<source>:6:8: warning: comparison of integer expressions of different signedness: 'unsigned int' and 'int' [-Wsign-compare]
    6 |  if (i != argc)
      |      ~~^~~~~~~
Compiler returned: 0

However, if I uncomment this const - the compiler is happy. I can reproduce this on almost every GCC version (see https://godbolt.org/z/b6eoc1). Is is this a bug in GCC?

4 Answers

I think that what you are missing is compiler optimization. Without const, that variable is a variable, meaning that it can change. Since it is an unsigned int, it means that indeed it can be larger than an integer.

const unsigned int i = 2147483648;

You will get your error back if you assign a value greater than the largest value of int to that unsigned int.

However if it is const, the compiler knows its value, it knows, that it will not change, and there will be no problem with the comparison.

If you take a look at the assembly, you will see that without const, it actually takes the value of the variable to compare:

movl    $0, -4(%rbp)
movl    -20(%rbp), %eax
cmpl    %eax, -4(%rbp)

Now, if it is const, it will not bother with the variable at all, it just takes the value:

movl    $0, -4(%rbp)
cmpl    $0, -20(%rbp)

I'd say it's a compiler bug in the -Wsign-compare option.

Test by compiling your example with -Wall -Wextra -O3. With -O3 added, the warning suddenly goes away in the const case. Even though the generated machine code with or without const is identical. This doesn't make any sense.

Naturally, neither const nor the generated machine code has any effect on the signedness of the C operands, so the warning shouldn't come inconsistently depending on type qualifiers or optimizer settings.

Simple use -Wall -Wextra and you will get your warning back.

I would advise using -Wall -Wextra -pedantic compiler options

https://godbolt.org/z/TvqeKn

EDIT

As clarification to the very unfriendly and unkind OP comment. -Wextra enables the following warnings including the one which OP wants

enter image description here

warning: comparison of integer expressions of different signedness:
 'unsigned int' and 'int' [-Wsign-compare]
    9 |     if (i != argc)

The question is tagged C but links to a C++ Godbolt example. Here is a table showing when the warning is issued:1

C non-const C const C++ non-const C++ const
Default warnings No No No No
-Wall No No Yes No
-Wall -Wextra Yes Yes Yes No

So, in C, GCC provides the warning in -Wextra regardless of const qualification.

In C++, GCC provides the warning in -Wall but treats const qualified objects as known values for which the warning may be suppressed.

The GCC documentation says, for -Wsign-compare:

Warn when a comparison between signed and unsigned values could produce an incorrect result when the signed value is converted to unsigned. In C++, this warning is also enabled by -Wall. In C, it is also enabled by -Wextra.

Note that it does not say it warns when there is a comparison between signed and unsigned values but rather when such a comparison could produce an incorrect result. Therefore, not providing a warning when the definition of the object is such that the comparison cannot produce an incorrect result is not a bug.

The word “could” leaves latitude for what the compiler “knows” about the object. Failing to determine the C const case cannot produce an incorrect result could be described as a bug although it may be better described as a shortcoming.

Footnote

1 “Const” in the table is specifically use of an object that is const-qualified and whose value is immediately available to the compiler via a visible definition. I did not test cases where, for example, an identifier is declared for a const-qualified object but its definition is in another translation unit.

Related