bobbay bobbay - 1 month ago 4x
C Question

Why cast to a pointer then dereference?

I was going through this example which has a function outputting a hex bit pattern to represent an arbitrary float.

void ExamineFloat(float fValue)
printf("%08lx\n", *(unsigned long *)&fValue);

Why take the address of fValue, cast to unsigned long pointer, then dereference? Isn't all that work just equivalent to a direct cast to unsigned long?

printf("%08lx\n", (unsigned long)fValue);

I tried it and the answer isn't the same, so confused.

(unsigned long)fValue

This converts the float value to an unsigned long value, according to the "usual arithmetic conversions".

*(unsigned long *)&fValue

The intention here is to take the address at which fValue is stored, pretend that there is not a float but an unsigned long at this address, and to then read that unsigned long. The purpose is to examine the bit pattern which is used to store the float in memory.

As shown, this causes undefined behavior though.

Reason: You may not access an object through a pointer to a type that is not "compatible" to the object's type. "Compatible" types are for example (unsigned) char and every other type, or structures that share the same initial members (speaking of C here). See ยง6.5/7 N1570 for the detailed (C11) list (Note that my use of "compatible" is different - more broad - than in the referenced text.)

Solution: Cast to unsigned char *, access the individual bytes of the object and assemble an unsigned long out of them:

unsigned long pattern = 0;
unsigned char * access = (unsigned char *)&fValue;
for (size_t i = 0; i < sizeof(float); ++i) {
  pattern |= *access;
  pattern <<= CHAR_BIT;

Note that (as @CodesInChaos pointed out) the above treats the floating point value as being stored with its most significant byte first ("big endian"). If your system uses a different byte order for floating point values you'd need to adjust to that (or rearrange the bytes of above unsigned long, whatever's more practical to you).