Reputation: 2669
I have the following C code:
unsigned int a;
unsigned char b, c;
void test(void) {
if (a < b)
return;
if (a < (b ? b : c))
return;
}
When I compile it (with Microsoft cl
, from MS SDK 7, -W3
warning level), the second comparison emits a warning: C4018, signed/unsigned mismatch. The first comparison emits no warning.
I've checked MS docs on the conditional operator and they says that if both operands are of same type, the result will be of the same type, so it should work as the first comparison. Am I missing something?
UPD: tested with gcc -Wall -Wextra -pedantic
and got no warnings whatsoever.
Upvotes: 8
Views: 7471
Reputation: 169563
This is probably due to the arithmetic conversion rules: First, any integer type of conversion rank less than int
(eg unsigned char
) will promote to int
or unsigned int
.
Whether the result will be int
or unsigned int
does not (directly) depend on the signedness of the original type, but its range: int
is used even for unsigned types as long as all values can be represented, which is the case for unsigned char
on mainstream architectures.
Second, as both operands end up with the same conversion rank, but one is unsigned, the other operand will be converted to an unsigned type as well.
Semantically, your expressions read
a < (unsigned int)(int)b
and
a < (unsigned int)(b ? (int)b : (int)c)
The compiler is apparently smart enough to notice that the first case cannot cause problems, but fails for the second one.
Steve Jessop's comment nicely explains how this could happen:
I would imagine that in the first case the compiler thinks, "I have a comparison operator whose operand types are
unsigned int
andunsigned char
. No need for a warning, now let's apply promotion followed by usual conversion".In the second case it thinks, "I have a comparison operator whose operand types are
unsigned int
andint
(which I derived as the type of the conditional expression on the RHS). Best warn about that!".
Upvotes: 8
Reputation: 279245
The rules are different between C and C++. The appropriate standard can be difficult to judge when compiling C with MSVC, but fortunately in this case C89 and C99 are the same.
In C89 3.3.15:
If both the second and third operands have arithmetic type, the usual arithmetic conversions are performed to bring them to a common type and the result has that type
In C99 6.5.15/5:
If both the second and third operands have arithmetic type, the result type that would be determined by the usual arithmetic conversions, were they applied to those two operands, is the type of the result.
In C++03 5.16/4:
If the second and third operands are lvalues and have the same type, the result is of that type and is an lvalue
So when you, say, "if both operands are of same type, the result will be of the same type, so it should work as the first comparison", that only applies to C++, not to C. In C the type of the RHS of that comparison is int
. In C++ the RHS would be an lvalue of type unsigned char
as you expected.
Upvotes: 1
Reputation: 213711
if (a < b)
equals the pseudo if (unsigned int < unsigned char)
.
Whenever a char type is used in an expression the integer promotion rules in C implicitly converts it to an int
. After that is done, you have
if (unsigned int < int)
.
Whenever two integers of same rank but different signedness are used in an expression, the signed one gets implicitly converted to unsigned. This is determined by the usual arithmetic conversions, aka balancing.
So your first expression is converted to
if (unsigned int < unsigned int)
before anything is done.
In the second expression we have if (a < (b ? b : c))
which equals pseudo
if (unsigned int < (unsigned char ? unsigned char : unsigned char))
.
Integer promotions are performed on all chars, so it gets implicitly converted to
if (unsigned int < (int ? int : int))
.
Then a weird, obscure rule for the conditional operator dictactes that the 2nd and 3rd operator of the ?: operator must be balanced with the usual arithmetic conversions. In this case they are already of the same type, so nothing happens. We end up with
if (unsigned int < int)
Balancing occurs again, the result will be evaluated as
if (unsigned int < unsigned int)
When I compile it with Microsoft
When you compile with Microsoft, all bets are off, their compiler is very poor at following the standard. Expect weird, illogical warnings.
Upvotes: 3