Mikhail Edoshin
Mikhail Edoshin

Reputation: 2669

Signed/unsigned mismatch when comparing two unsigned values using a conditional operator

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

Answers (3)

Christoph
Christoph

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 and unsigned 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 and int (which I derived as the type of the conditional expression on the RHS). Best warn about that!".

Upvotes: 8

Steve Jessop
Steve Jessop

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

Lundin
Lundin

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

Related Questions