Бинарные операции между различными целочисленными типами выполняются в рамках «общего» типа, определяемого так называемыми обычными арифметическими преобразованиями (см. Спецификацию языка, 6.3.1.8). В вашем случае «общий» тип - unsigned int
. Это означает, что int
операнд (ваш b
) будет преобразован в unsigned int
перед сравнением, а также с целью выполнения вычитания.
Когда -1
преобразуется в unsigned int
, результатом является максимально возможное значение unsigned int
(аналогично UINT_MAX
). Излишне говорить, что оно будет больше, чем ваше значение без знака 1000
, означающее, что a > b
действительно ложно, а a
действительно мало по сравнению с (unsigned) b
. if
в вашем коде должно преобразовываться в ветвь else
, что вы и наблюдали в своем эксперименте.
Те же правила преобразования применяются к вычитанию. Ваш a-b
действительно интерпретируется как a - (unsigned) b
, а результат имеет тип unsigned int
. Такое значение не может быть напечатано с помощью спецификатора формата %d
, поскольку %d
работает только со значениями со знаком . Ваша попытка напечатать его с помощью %d
приводит к неопределенному поведению, поэтому значение, которое вы видите напечатанным (даже несмотря на то, что оно имеет логическое детерминированное объяснение на практике), совершенно бессмысленно с точки зрения языка Си.
Редактировать: На самом деле, я могу ошибаться в отношении неопределенной части поведения. В соответствии со спецификацией языка C общая часть диапазона соответствующего целочисленного типа со знаком и без знака должна иметь идентичное представление (что подразумевает, согласно сноске 31, «взаимозаменяемость в качестве аргументов функций»). Таким образом, результатом выражения a - b
является беззнаковое 1001
, как описано выше, и, если я что-то упустил, допустимо напечатать это конкретное беззнаковое значение со спецификатором %d
, поскольку оно попадает в положительный диапазон int
. Печать (unsigned) INT_MAX + 1
с %d
будет неопределенной, но 1001u
- это нормально.