Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C not run a comparison of unsigned int with negative value?

Consider this C code:

#include "stdio.h"

int main(void) {

    int count = 5;
    unsigned int i;

    for (i = count; i > -1; i--) {
        printf("%d\n", i);
    }
    return 0;
}

My observation/question: the loop never gets executed. But if I change the data type of i from unsigned int to int, everything works as expected.

I've been thinking of unsigned ints as values that "wrap around" when you try to keep subtracting from them. So, when i is zero and I subtract 1, it would wrap around to UINT_MAX. And since its value is never negative, this would be effectively an endless loop. (And this is exactly what happens when I change the comparison from i > -1 to i >= 0.)

There is a fault somewhere in my logic, as the loop never gets executed if i is unsigned, and I'm comparing it to -1. Either the compiler optimizes it away somehow or the runtime values behave differently from what I expect.

Why does the loop not get run?

like image 807
Jaanus Avatar asked Nov 29 '22 04:11

Jaanus


2 Answers

In i > -1, the -1 is converted to unsigned int, resulting in the value UINT_MAX. i is never bigger than that value, so the loop body never executes.

You might find that you can persuade your compiler to warn you about this: use of an always-true or always-false expression in a conditional context. But that still wouldn't help you if you'd written i > -2, so you may also find you can enable a warning for all mixed-sign comparisons.

Note that in C, arithmetic is always performed with operands of equal type. This includes comparisons but IIRC not the shift operators. If the operands are of different type, as in this case, then at least one of them is converted to make them the same type. The rules for working out the destination type are in 6.3.1.1/2 and 6.3.1.8/1.

like image 56
Steve Jessop Avatar answered Dec 15 '22 14:12

Steve Jessop


When you mix signed and unsigned operands of the same width in a "type-symmetrical" binary operation (like +, * or > in your example), the unsigned type "wins" and the operation is evaluated in unsigned domain. I.e. signed operand gets converted to unsigned type.

In your example the integer constant has type signed int, while i has type unsigned int. Operands have the same width, so in your example i > -1 is interpreted as i > (unsigned) -1, which is equivalent to i > UINT_MAX. This is why your loop is never executed.

like image 26
AnT Avatar answered Dec 15 '22 16:12

AnT