Comparison operation on unsigned and signed integers
Binary operations between different integral types are performed within a "common" type defined by so called usual arithmetic conversions (see the language specification, 6.3.1.8). In your case the "common" type is unsigned int
. This means that int
operand (your b
) will get converted to unsigned int
before the comparison, as well as for the purpose of performing subtraction.
When -1
is converted to unsigned int
the result is the maximal possible unsigned int
value (same as UINT_MAX
). Needless to say, it is going to be greater than your unsigned 1000
value, meaning that a > b
is indeed false and a
is indeed small compared to (unsigned) b
. The if
in your code should resolve to else
branch, which is what you observed in your experiment.
The same conversion rules apply to subtraction. Your a-b
is really interpreted as a - (unsigned) b
and the result has type unsigned int
. Such value cannot be printed with %d
format specifier, since %d
only works with signed values. Your attempt to print it with %d
results in undefined behavior, so the value that you see printed (even though it has a logical deterministic explanation in practice) is completely meaningless from the point of view of C language.
Edit: Actually, I could be wrong about the undefined behavior part. According to C language specification, the common part of the range of the corresponding signed and unsigned integer type shall have identical representation (implying, according to the footnote 31, "interchangeability as arguments to functions"). So, the result of a - b
expression is unsigned 1001
as described above, and unless I'm missing something, it is legal to print this specific unsigned value with %d
specifier, since it falls within the positive range of int
. Printing (unsigned) INT_MAX + 1
with %d
would be undefined, but 1001u
is fine.
Detecting signed vs. unsigned comparison bugs
You can use -Wsign-compare
with -Wsign-conversion
.
The first one warns you when you compare signed values with unsigned ones. The latter warns you about implicit conversions from unsigned to signed and from signed to unsigned.
In your case -Wsign-compare
won't do anything because of integer promotion, while -Wsign-conversion
will warn about implicit conversion in s16 = u16
.
A warning - comparison between signed and unsigned integer expressions
It is usually a good idea to declare variables as unsigned
or size_t
if they will be compared to sizes, to avoid this issue. Whenever possible, use the exact type you will be comparing against (for example, use std::string::size_type
when comparing with a std::string
's length).
Compilers give warnings about comparing signed and unsigned types because the ranges of signed and unsigned ints are different, and when they are compared to one another, the results can be surprising. If you have to make such a comparison, you should explicitly convert one of the values to a type compatible with the other, perhaps after checking to ensure that the conversion is valid. For example:
unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();
if (i >= 0)
{
// i is nonnegative, so it is safe to cast to unsigned value
if ((unsigned)i >= u)
iIsGreaterThanOrEqualToU();
else
iIsLessThanU();
}
else
{
iIsNegative();
}
Is comparing signed and unsigned integer safe? [duplicate]
Comparing unsigned with signed can have serious problems, e.g.
std::string( "Bah" ).size() < -5
… will always be true.
That's the reason why you get warnings, but the compiler isn't smart enough, in the the time frame it has allotted, to see that any particular such comparison would be safe.
Now to your bullet point questions.
” is there any benefit, performance or safety-wise, to always using unsigned over signed integers where applicable?
Yes, for correctness, avoiding UB. But, “applicable” is where you do bit level things. For numbers always prefer signed integer, or floating point type.
” in what cases, if any, comparing signed and unsigned integer can result in errors or unexpected behavior?
When unsigned wrap-around gets involved, typically by implicit promotion of an operand from signed to unsigned.
” if there is no good reason to worry about those warnings, is there any historical reason, why this information could be useful to developers in the past?
Not applicable.
comparing unsigned vs signed does not make a warning (using const)
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.
C++ Comparison between signed and unsigned integer expressions
The most straightforward way to fix this is to make i
an unsigned variable instead of a signed one. You can use size_t
to match the return type of strlen:
size_t length = strlen(m_sku);
char *arr = new char[length]();
for (size_t i = 0; i <= length; i++) {
arr[i] = m_sku[i];
}
But be careful since this same replacement doesn't work with loops that count down towards 0.
// oops! This is an infinite loop:
for (size_t i = length-1; i >=0; i--) {
arr[i] = m_sku[i];
}
Related Topics
Remove Max Value from Simply-Connected List
Sorting Characters in a String First by Frequency and Then Alphabetically
Why Use Apparently Meaningless Do-While and If-Else Statements in Macros
Is There a Difference Between Copy Initialization and Direct Initialization
Difference Between Const Int*, Const Int * Const, and Int Const *
How to Set, Clear, and Toggle a Single Bit
Why Can Templates Only Be Implemented in the Header File
What Are the Basic Rules and Idioms For Operator Overloading
Undefined Behavior and Sequence Points
What Is the Strict Aliasing Rule
Calling Virtual Functions Inside Constructors
Can Num++ Be Atomic For 'Int Num'
How to Get a Stack Trace for C++ Using Gcc With Line Number Information
What Does "Dereferencing" a Pointer Mean
Fastest Way to Check If a File Exists Using Standard C++/C++11,14,17/C