Unsigneds have 1) higher maximums and 2) defined, wraparound overflow.
If with infinite precision
(unxigned_c = unsigned_a + unsinged_b) >= UINT_MAX
then unsigned_c
will get reduced modulo UINT_MAX+1
:
#include <limits.h>
#include <stdio.h>
int main()
{
printf("%u\n", UINT_MAX+1); //prints 0
printf("%u\n", UINT_MAX+2); //prints 1
printf("%u\n", UINT_MAX+3); //prints 2
}
A a similar thing is happening with you storing signed values into an unsigned.
In this case 6.3.1.3p2 applies -- UINT_MAX+1
is conceptually added to the value).
With signed types, on the other hand, overflow is undefined, which means if you allow it to happen, your program is no longer well formed and the standard makes no guarantees about its behavior. Compilers exploit this for optimization by assuming it will never happen.
For example, if you compile
#include <limits.h>
#include <stdio.h>
__attribute__((noinline,noclone)) //or skip the attr & define it in another tu
_Bool a_plus1_gt_b(int a, int b) { return a + 1 > b; }
int main()
{
printf("%d\n", a_plus1_gt_b(INT_MAX,0)); //0
printf("%d\n", INT_MAX+1); //1
}
on gcc with -O3
, it'll very likely print
1
-2147483648