Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C, if I cast & dereference a pointer, does it matter which one I do first?

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?

like image 925
sleske Avatar asked Jan 08 '11 14:01

sleske


5 Answers

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).

like image 91
davep Avatar answered Oct 09 '22 10:10

davep


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.

like image 32
Ise Wisteria Avatar answered Sep 19 '22 15:09

Ise Wisteria


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.

like image 4
etarion Avatar answered Oct 09 '22 12:10

etarion


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.

like image 4
341008 Avatar answered Oct 09 '22 12:10

341008


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.

like image 2
Karl Knechtel Avatar answered Oct 09 '22 10:10

Karl Knechtel