I don't agree with you here.
Let's dissect : ( void * ) *( UInt32 * ) &data
It says to the compiler:
[ Consider only ( UInt32 * ) &data ]
Hey just assume that &data which used to point to a GadgetData
object in memory now points to an UInt32
Actually, this does not tell a compiler to "assume", but rather to
*convert*. That is, if &data has type "pointer to GadgetData" (and
some valid value of that type), the compiler must now convert that
value to a new value, of type "pointer to UInt32". There is no
requirement that the new value be useful in any way, in this
particular case. Only "void *" (and by extension "char *") have
constraints strong enough to enforce "usefulness" of such conversions.
(On machines with multiple pointer formats, the computer must often
execute at least one CPU instruction in order to perform the
conversions produced by pointer casts. On computers with only
one pointer format, the conversion takes no instructions, but
is still a conversion.)
Now consider :
( void * ) * .....
This says that take the object at this address ( now the compiler
thinks that since this address points to an UInt32 object) ...
More precisely, the compiler must follow the pointer value -- or
produce code to do this at runtime -- produced by the conversion
you requested earlier. For this operation to have a useful,
well-defined result, that pointer must in fact point to a valid
object of type "UInt32" (whatever that is -- presumably the same
as C99's uint32_t, though).
(assuming the pointer is valid and the access produces a useful
32-bit object, yes...)
and forcibly say that this is a void *
Once again, a cast does not mean "pretend it is" or even "it IS",
but rather "please convert it to". Conversions from integer to
pointer are implementation-defined:
[C99 draft, section 6.2.2.3]
An integer may be converted to any pointer type. The result
is implementation-defined, might not be properly aligned, and
might not point to an entity of the referenced type.
but are meant to do the "obvious thing" to anyone who is experienced
with the machine's internal representations. (On some machines,
reasonable people might even reasonably disagree as to what the
"obvious thing" should be, so even this is a little iffy.)
Ok now here's the biggest catch ... as to why I think this is such a
*bad* idea. It's simply that this just may not work at all. Check out
the fact that the sum of the fields of a struct may not equal the
sizeof the struct at all. So your basic assumption that this is a 32
bit struct may not always be true. As we go along theres a LOT of UB
out here... but the start itself is one.
Indeed. Additional possibilities include:
- the struct is 32 bits but is not properly aligned for access
as a "UInt32"
- the struct is 32 bits but "void *" is not
One case of the latter is rapidly becoming common today, with 64-bit
CPUs. Of course, 64 value are likely to be able to hold 32 value
bits without losing any bits, but the "thin ice" aspect of the
whole approach should be obvious by now.