I'm sorry if this question is too basic...I just have not found the answer to it anywhere.
Say I declare a C variable like this:
unsigned int var = 241;
In this case the var is unsigned so my intention is for it to have decimal value 241.
Alternatively I could declare it like this:
signed int var = -15;
In this case I declared it as signed integer so, as per my understanding it should have the decimal value -15.
However both times, I assume the var will be declared in memory(hardware) like this: 1111 0001.
So how does the processor know, at the lowest level which is in the hardware that I intended to declare this as 241 or -15? I'm aware of the two's complement notation that is used to represent negative numbers and such but, I assume in hardware the processor only sees a sequence of ones and zeroes and then does some operation with it by switching the states of some ICs. How does the processor know whether to interpret the sequence of bits in standard binary(for unsigned) or 2's complement(for signed)?
Also another somewhat unrelated questions:
In C I can do this:
unsigned int var = -15; printf("The var is: %d ", var); This will as expected print -15. Why, when I do this:
signed int var = 0xF1; //or 0b11110001 printf("The var is: %d ", var);
I get 241 instead of -15? Since I declared it as signed and in two's complement 0xF1 is -15 why am I getting the value 241 which is the equivalent of 0xF1 in standard binary?
Shouldn't it throw an error telling me I can't assign negative values to a variable which I have declared as unsigned?
Thank you and I apologize for my many and perhaps basic questions, there is so much I do not know :D.
If the sign bit is 1 then the number is negative, although formats other than two's complement integers allow a signed zero: distinct "positive zero" and "negative zero" representations, the latter of which does not correspond to the mathematical concept of a negative number.
The sign of the number depends on the highest bit set (assuming two's complement representation, which is used by the vast majority of systems currently in use), so a memory address above 0x80000000 on a 32-bit system will be negative, and a memory address below 0x80000000 will be positive.
Signed and unsigned use the same data, but different instructions. The computer stores signed and unsigned integers as the same data.
The hardware does not know.
The compiler knows.
The compiler knows because you said so here signed int var = -15;
, "This, dear compiler, is a variable which can be negative and I init it to a negative value."
Here you said differently unsigned int var = 241;
, "This, dear compiler, is a variable which cannot be negative and I init it to a positive value."
The compiler will keep that in mind for anything you later do with the variable and its values. The compiler will turn all corresponding code into that set of instructions in machine language, which will cause the hardware to behave accordingly. So the hardware ends up doing things appropriate to negative or not; not because of knowing, but because of not getting a choice on it.
An interesting aspect of "corresponding instructions" (as pointed out by Peter Cordes in a comment below) is the fact that for the special (but very widely used) case of 2-complement representation of negative values, the instructions are actually identical for both (which is an important advantage of 2-complement).
If the two values were char
(signed or not), then their internal representation (8-bit pattern) would be the same in memory or register.
The only difference would be in the instructions the compiler emits when dealing with such values.
For example, if these values are stored in variables declared signed
or unsigned
in C
, then a comparison between such values would make the compiler generate a signed or unsigned specific comparison instruction at assembly level.
But in your example you use int
s.
Assuming that on your platform these int
s use four bytes, then the two constants you gave are not identical when it comes to their 32-bit pattern.
The higher bits take in consideration the sign of the value and propagate to fill with 0 or 1 up to 32-bits (see the sequences of 0
or f
below).
Note that assigning a negative value to an unsigned int
produces a warning at compilation if you use the proper compiler flags (-Wconversion
for example).
In his comment below, @PeterCordes reminds us that such an assignment is legal in C, and useful in some situations; the usage (or not) of compiler flags to detect (or not) such cases is only a matter of personal choice.
However, assigning -15U
instead of -15
makes explicit the intention to consider the constant as unsigned (despite the minus sign), and does not trigger the warning.
int i1=-15;
int i2=0xF1;
int i3=241;
printf("%.8x %d\n", i1, i1); // fffffff1 -15
printf("%.8x %d\n", i2, i2); // 000000f1 241
printf("%.8x %d\n", i3, i3); // 000000f1 241
unsigned int u1=-15; // warning: unsigned conversion from ‘int’ to ‘unsigned int’ changes value from ‘-15’ to ‘4294967281’
unsigned int u2=0xF1;
unsigned int u3=241;
printf("%.8x %u\n", u1, u1); // fffffff1 4294967281
printf("%.8x %u\n", u2, u2); // 000000f1 241
printf("%.8x %u\n", u3, u3); // 000000f1 241
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With