Counting / 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 produces a warning: C4018, signature / unsigned mismatch. The first comparison does not give a warning.

I tested MS docs on a conditional statement , and they say that if both operands are of the same type, the result will be the same type, so it should work as the first comparison. Did I miss something?

UPD: tested with gcc -Wall -Wextra -pedantic and did not receive any warnings.

+7
source share
3 answers

This is probably due to the rules of arithmetic conversion: first, any integer type of conversion rank less than int (e.g. unsigned char ) will advance to int or unsigned int .

Whether the result of int or unsigned int does not (directly) depend on the subscription of the original type, but its range: int used even for unsigned types if all values ​​can be represented, which is the case for unsigned char on the main architectures.

Secondly, since both operands end with the same conversion rank but one unsigned, the other operand will also be converted to an unsigned type.

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 does not work for the second.

Steve Jessop's comment perfectly explains how this can happen:

I would suggest that in the first case, the compiler thinks: "I have a comparison operator whose operand types are unsigned int and unsigned char . There is no need for a warning, now you can apply the promotion, followed by the usual conversion."

In the second case, he thinks: "I have a comparison operator whose operand types are unsigned int and int (which I got as a conditional expression type on RHS). Better warn about it!".

+7
source

if (a < b) is equal to pseudo if (unsigned int < unsigned char) .

Whenever a char is used in an expression, the whole promotion rules in C implicitly convert it to int . After that you

if (unsigned int < int) .

When an expression uses two integers with the same rank but with a different signature, the signed one gets implicitly converted to unsigned. This is determined by ordinary arithmetic conversions, as well as balancing.

So your first expression is converted to

if (unsigned int < unsigned int)

before anything is done.


In the second expression, if (a < (b ? b : c)) , which is equal to pseudo

if (unsigned int < (unsigned char ? unsigned char : unsigned char)) .

Integer promotions run on all characters, so they are implicitly converted to

if (unsigned int < (int ? int : int)) .

Then the strange, obscure rule for the conditional operator dictates that the 2nd and 3rd operator of the operator ?: must be balanced with the usual arithmetic transformations. In this case, they are already of the same type, so nothing happens. As a result, we get

if (unsigned int < int)

Balancing happens again, the result will be evaluated as

if (unsigned int < unsigned int)


When I compile it with Microsoft

When compiling with Microsoft, all bets are disabled, their compiler very poorly complies with the standard. Expect strange, illogical warnings.

+3
source

Rules differ between C and C ++. The corresponding standard may 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 are of arithmetic type, ordinary arithmetic conversions are performed to bring them to a common type and the result is of this type

In C99 6.5.15 / 5:

If both the second and third operands are of arithmetic type, the result is the type that will be determined by the usual arithmetic transformations, whether they were applied to these two operands, is the result type.

In C ++ 03 5.16 / 4:

If the second and third operands are lvalues ​​and have the same type, the result of this type is lvalue

So, when you say, “if both operands are of the same type, the result will be of the same type, so it should work as the first comparison”, which applies only to C ++, not C. In C, the RHS type of this comparison is int . In C ++ RHS there will be an lvalue of type unsigned char , as you expected.

+1
source

All Articles