Reputation: 125
I thought that unsigned int could store only integers >= 0. But I tried assigning a negative to an unsigned int, nothing special happened. It seems like it stored the value with no problem.
So what is the difference between signed and unsigned int, and what's the point if it can store any value anyway?
Upvotes: 10
Views: 4107
Reputation: 42139
One important point is that overflowing a signed integer is undefined behaviour, whereas unsigned integers are defined to wrap around. In fact that is what is happening when you assign a negative value to one: it simply wraps around until the value is in range.
While this wrap-around behaviour of unsigned types means it is indeed perfectly valid to assign negative values to them, converting them back to signed types is not as well-defined (at best it is implementation-defined, at worst undefined behaviour, depending on how you do it). And while it maybe even be true that on many common platforms the signed and unsigned integers are internally the same, the intended meaning of the value matters for comparisons, conversions (such as to floating point), as well as for compiler optimisation.
In summary, you should use an unsigned type when you need well-defined wrap-around semantics for over- and underflow, and/or you need to represent positive integers greater than the maximum of the corresponding (or largest suitable) signed type. Technically you could avoid signed types in most cases by implementing negative numbers on top of unsigned types (after all, you could simply choose to interpret certain bit patterns as negative numbers), but… why, when the language offers this service "for free". The only real problem with signed integers in C is having to watch out for overflow, but in return you may get better optimisation.
Upvotes: 3
Reputation: 11020
The point of using unsigned int in C is that:
Upvotes: 3
Reputation: 60068
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
Upvotes: 1
Reputation: 35154
A statement like
unsigned int t = -1;
printf("%u", t);
is completely legal and well defined in C. Negative values, when assigned to an unsigned integral type, get implicitly converted (cf. for example, this online C standard draft):
6.3.1.3 Signed and unsigned integers
(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.
The output of above program is an unsigned value, i.e.
4294967295
So you can assign "negative" values to unsigned integral types, yet the result is not a negative value in its actual sense. This is particularly relevant when you compare unsigned integral values to negative values. Consider, for example, the following two loops:
int i = 10;
while (--i >= 0) { // 10 iterations
printf("i: %d\n", i);
}
unsigned int u = 10;
while (--u >= 0) { // endless loop; warning provided.
printf("u: %u\n", u);
}
The first will finish after 10 iterations, whereas the second will never end: Unsigned integral values cannot become negative, so u >= 0
is always true.
Upvotes: 9
Reputation: 890
You are correct that unsigned int
can only store integers >= 0. (Of course there is an upper limit too, and that upper limit depends on your architecture and is defined as UINT_MAX
in limits.h).
By assigning an signed int
value to an unsigned int
, you are invoking an implicit type conversion. The C language has some very precise rules about how this happens. Whenever possible, the compiler attempts to preserve the value whenever possible. Take this for instance:
int x = 5;
unsigned int y;
y = x;
The above code also does a type conversion, but since the value "5" is representable in both signed and unsigned integer ranges, the value can be preserved, so y
will also have a value of 5.
Now consider:
x = -5;
y = x;
Specifically in this case you are assigning a value that is not within the representable range of unsigned int
, and therefore the compiler must convert the value to something within range. The C standard dictates that the value 1 + UINT_MAX
will be added to the value until it is within range of unsigned int
. On most systems these days, UINT_MAX
is defined as 4294967925 (2^32 - 1), so the value of y
will actually be 4294967921 (or 0xFFFFFFFB in hex).
It is important to note that on twos-complement machines (nearly ubiquitous these days) the binary representations of a signed int
value of -5 is also 0xFFFFFFFB, but that is not required. The C standard allows for and supports machines that utilize different integer encodings, therefore portable code should never assume that the binary representation will be preserved after an implicit conversion such as this.
Hope this helps!
Upvotes: 3