How does the casting of primitive datatypes works?
The restriction "primitive datatype" is not enough:
Casting from a 16-bit integer to a 32-bit integer works differently than casting between integer types of the same size. And casting between integer and floating-point types is even more complex.
This Code does work fine. What about this code?
Both code examples will work differently on different CPUs, operating systems and possibly using different compilers.
This is because under some operating systems (e.g. 32-bit Windows) long
and unsigned
data types have the same number of bits; under other operating systems (e.g. 64-bit Windows) long
has twice as many bits as unsigned
.
And you use the ~
operator (~n)
on a signed data type which may cause different results depending on the compiler you use.
This code indeed prints out negative numbers.
So is there an (architectural) way, to explain this phenomenon?
There are two effects:
The first effect is:
If you pass a value of a wrong data type to some function, C will automatically cast the value.
And if you are casting a value that is outside the range of the data type you are casting to, the result will typically not be what you are intuitively expecting.
In the case of two integer data types, normally the "rightmost" bits of the value are copied to the new value.
Let's take (signed char)3478
as an example:
The number 3478 is written as 110110010110
in binary. signed char
is an 8 bit data type. The rightmost 8 bits of 1101 10010110
are 10010110
. And the value (-106) is stored as 10010110
in a signed char
variable.
Therefore (signed char)3478
will result in (-106).
The value a
is stored in an unsigned long
variable which means that it cannot be negative!
However, you say that you "print out" the value of a
. This means you pass the value to some function that prints it to the screen.
Obviously that function expects some signed data type. C will cast the value stored in a
to the signed data type and as in the example with the number 3478, the result of casting may be a negative number.
If you use printf
you may see a second effect:
If some function (such as printf
) has a variable number of parameters, the way the parameters are passed to the function is depending on the data types passed. (The details may also vary from OS to OS.)
You may now pass the wrong data type to the function; for example you use "%d"
(signed int
) in the printf
format string but you pass an unsigned long
.
If you do this, the compiler will write the value of a
to some data storage (memory or register) which is suitable for unsigned long
values before actually calling the function. (For such functions the compiler is not able to find out which data type is really expected by the function, so it has to assume that the function expects the data type you are passing to the function.)
However, printf
will look for a value in some data storage suitable for signed int
values which might be located somewhere completely different. That data storage does not contain any useful value because the compiler wrote the value of a
somewhere else!