1
#include <stdio.h>

int main() {

    unsigned int i = 23;
    signed char c = -23;
    if (i<c)
       puts("TRUE");

    return 0;
}

Why the output of the following program is TRUE, even though I have used signed char which can store from -128 to 127.

devXm
  • 27
  • 5
  • 2
    Default integer promotions. Both variables are converted to `unsigned int`, and `-23` as an `unsigned int` is a lot bigger than `23`. Print the values to see that. See C11 [§6.3.1.1 Conversions / Arithmetic operands / Booleans, characters and integers](http://port70.net/~nsz/c/c11/n1570.html#6.3.1.1) and [§6.3.1.8 Usual arithmetic conversions](http://port70.net/~nsz/c/c11/n1570.html#6.3.1.8). – Jonathan Leffler May 30 '21 at 21:28
  • Note, this is why its dangerous to compare types with different signs. It's strongly advised to compile with the `-Wextra` flag, both gcc and clang generate a warning when such a comparison is discovered. gcc's is _warning: comparison of integer expressions of different signedness: 'unsigned int' and 'signed char' [-Wsign-compare]_, clang's is similar. – yano May 30 '21 at 22:42

2 Answers2

6

Your i < c comparison has operands of two different types, so the operand with the smaller type (lower rank) is converted to the type of the other. In this case, as -23 cannot be represented as an unsigned int, and following the "usual arithmetic conversions," it will end up with a value considerably greater than +23 (actually, UINT_MAX + 1 - 23), according to Paragraph #2 in the following excerpt from this (Draft) C11 Standard:

6.3.1.3 Signed and unsigned integers

1    When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
2    Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
3    Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

You can see this conversion in action by assigning c to a separate unsigned int and printing its value, as in the following code. Assuming a 32-bit unsigned int, you will likely see a value of 4294967273 (which is, indeed, greater than 23).

#include <stdio.h>

int main()
{
    unsigned int i = 23;
    signed char c = -23;
    unsigned int uc = c; // Add a diagnostic line ...
    printf("%u\n", uc);  // ... and show the value being used in the comparison
    if (i < c) {
        printf("TRUE");
    }
    return 0;
}

Note: The relative size of the two variables (i and c) here is something of a red herring; even if you declare c as signed int c = -23;, you will still get the same result, due to the Usual Arithmetic Conversions (link courtesy of Jonathan Leffler's comment) – note that a signed int cannot represent all possible values of an unsigned int, so the very last of the 'hollow' bullets will come into play.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
4

In order to make the comparison, the smallest variable (char, 8 bit) must be promoted to the size and signedness of the larger one (integer, 32 bit). But in so doing, -23 becomes 4294967273. Which is much more than 23.

If you compare c with a signed integer, the sign promotion causes no change and the test behaves as expected.

LSerni
  • 55,617
  • 10
  • 65
  • 107