Could anyone help me understand the difference between signed/unsigned int, as well as signed/unsigned char? In this case, if it's unsigned wouldn't the value just never reach a negative number and continue on an infinite loop of 0's?
int main()
{
unsigned int n=3;
while (n>=0)
{
printf ("%d",n);
n=n-1;
}
return 0;
}
CodePudding user response:
wouldn't the value just never reach a negative number
Correct, it can't be negative.
and continue on an infinite loop of 0's
No, it will wrap-around from zero to the largest value of an unsigned int
, which is well-defined behavior. If you use the correct conversion specifier %u
instead of the incorrect %d
, you'll notice this output:
3
2
1
0
4294967295
4294967294
...
CodePudding user response:
A signed integer can contain positive or negative values.
The integer type in C and C , both signed and unsigned, typically has 16 bits. That means that they are both able to hold 2^16 (65,536) unique numbers.
The possible values of the signed integer type range from -32,768 (-2^15) to 32,767 (2^15-1). If you were to count the number of integers in from -32,768 to 32,767, there would be 65,536.
On the other hand the unsigned integer type holds a minimum value of 0, and a maximum of 65,535 (2^16 - 1).
The char type, both signed and unsigned, represent a single byte (eight bits). It can hold 256 values. For the signed char type, the values range from -128 to 127. For the unsigned char type, the values range from 0 to 255.
Now, in regards to that code you posted. Yes, it will run forever. No, it will not print zeroes infinitely. When you have an unsigned integer (or any other type which stores integer values) with a value of zero, and you subtract one, you end up with the maximum value for that type.
The same also holds true for signed integer. If you have a signed integer which is storing the lowest possible value that it can hold, and subtract one, it will become the highest value.
CodePudding user response:
The terms signed and unsigned refer to how the CPU treats sequences of bits.
There are 2 important things to understand here:
- How the CPU adds finite sequences of bits to a single finite result
- How the CPU differentiates between signed and unsigned operands
Let's start with (1).
Let's take 4-bit nibbles for example.
If we ask the CPU to add 0001
and 0001
, the result should be 2, or 0010
.
But if we ask it to add 1111
and 0001
, the result should be 16, or 10000
. But it only has 4 bits to contain the result. The convention is to wrap-around, or circle, back to 0, effectively ignoring the Most Significant Bit. See also integer overflow..
Why is this relevant? Because it produces an interesting result. That is, according to the definition above, if we let x = 1111
, then we get x 1 = 0
. Well, x
, or 1111
, now looks and behaves awfully like -1
. This is the birth of signed numbers and operations. And if 1111
can be deemed as -1
, then 1111 - 1 = 1110
should be -2
, and so on.
Now let's look at (2).
When the C compiler sees you defining an unsigned int
, it will use special CPU instructions for dealing with unsigned numbers, where it deems relevant. For example, this is relevant in jump
instructions, where the CPU needs to know if you mean it to jump way forward, or slightly backward. For this it needs to know if you mean your operand to be interpreted in a signed, or unsigned way.
The operation of adding two numbers, on the other hand, is fundamentally oblivious to the consequent interpretation. The only thing is that the CPU will turn on a special flag after an addition operation, to tell you whether a wrap-around has occurred, for your own auditing.
But the important thing to understand is that the sequence of bits doesn't change; only its interpretation.
To tie all of this to your example, subtracting 1
from an unsigned 0
will simply wrap-around back to 1111
, or 2^32 in your case.
Finally, there are other uses for signed/unsigned. For example, by the very fact it is defined as a different type, this allows functions to be written that define a contract where only unsigned integers, let's say, can be passed to it. Also, it's relevant when you want to display or print the number.