In C, you can cast both simple data types like int
, float
, and pointers to these.
Now I would have assumed that if you want to convert from a pointer to one type to the value of another type (e.g. from *float
to int
), the order of casting and dereferencing does not matter. I.e. that for a variable float* pf
, you have (int) *pf == *((int*) pf)
. Sort of like commutativity in mathematics...
However this does not seem to be the case. I wrote a test program:
#include <stdio.h>
int main(int argc, char *argv[]){
float f = 3.3;
float* pf = &f;
int i1 = (int) (*pf);
int i2 = *((int*) pf);
printf("1: %d, 2: %d\n", i1, i2);
return 0;
}
and on my system the output is
1: 3, 2: 1079194419
So casting the pointer seems to work differently from casting the value.
Why is that? Why does the second version not do what I think it should?
And is this platform-dependent, or am I somehow invoking undefined behaviour?
The following says get the float at pf and convert it to an integer. The casting here is a request to convert the float into an integer. The compiler produces code to convert the float value to an integer value. (Converting float values to integers is a "normal" thing to do.)
int i1 = (int) (*pf);
The following says first FORCE the compiler to think pf points to an integer (and ignore the fact that pf is a pointer to a float) and then get the integer value (but it isn't an integer value). This is an odd and dangerous thing to do. The casting in this case DISABLES the proper conversion. The compiler performs a simple copy of the bits at the memory (producing trash). (And there could be memory alignment issues too!)
int i2 = *((int*) pf);
In the second statement, you are not "converting" pointers. You are telling the compiler what the memory points to (which, in this example, is wrong).
These two statements are doing very different things!
Keep in mind that c some times uses the same syntax to describe different operations.
=============
Note that double is the default floating point type in C (the math library typically uses double arguments).
According to 6.5/7 of the standard, roughly speaking,
stored value has to be compatible with the effective type
or a character type.
So, I think the statement int i2 = *((int*) pf)
isn't well-defined.
If you dereference first, cast to int later, you will get the usual (truncation) behavior of casts from float to int. If you cast to pointer to int first, then dereference, the behaviour isn't defined by the standard. Usually it manifests in interpreting the memory that contains the float as int. See http://en.wikipedia.org/wiki/IEEE_754-2008 for how that works out.
Of course it does! Casting tells the compiler how to look at the some section of memory. When you then access the memory, it tries to interpret the data there based on how you told it look at it. It's easier to understand it using the following example.
int main()
{
char A[] = {0, 0, 0, 1 };
int p = *((int*)A);
int i = (int)*A;
printf("%d %d\n", i, p);
return 0;
}
The output on a 32-bit little endian machine will be 0 16777216
. This is because (int*)A
tells the compiler to treat A as a pointer to integer and hence when you dereference A, it looks at the 4 bytes starting from A (as sizeof(int) == 4)
. After accounting for the endian-ness, the content of the 4 bytes evaluated to 16777216. On the other hand, *A
dereferences A to get 0
and (int)*A
casts that to get 0.
Cast, then dereference means "pretend I am pointing to this other thing, and then get the other thing that is supposedly being pointed at".
Dereference, then cast means "get the thing I am actually pointing at, and then convert it to this other thing by the usual rules".
"The usual rules" can change the bits around in order to get a value of another type that logically represents the same value. Pretending you're pointing at something you're not actually pointing at cannot.
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