Consider following example:
#include
int main(void)
{
unsigned char a = 15; /* one byte */
unsigned short b = 15; /* two bytes */
This behavior is correct. Quotes are from C 9899:TC2.
6.5.3.3/3:
The result of the unary
-operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.
6.2.5/9:
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
6.3.1.1/2:
The following may be used in an expression wherever an
intorunsigned intmay be used:
An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of
intandunsigned int.A bit-field of type
_Bool,int,signed int, orunsigned int.If an
intcan represent all values of the original type, the value is converted to anint; otherwise, it is converted to anunsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
So for long x = -a;, since the operand a, an unsigned char, has conversion rank less than the rank of int and unsigned int, and all unsigned char values can be represented as int (on your platform), we first promote to type int. The negative of that is simple: the int with value -15.
Same logic for unsigned short (on your platform).
The unsigned int c is not changed by promotion. So the value of -c is calculated using modular arithmetic, giving the result UINT_MAX-14.