Casting a void * into a 32-bit struct

L

Luc Le Blanc

I have 2 APIs that store/recall a void *. Since all I need to store is a
32-bit struct, I pass the actual data (instead of a pointer to it) as a
void *:

typedef
{
UInt8 color;
UInt8 index;
UInt16 resID;
} GadgetData;

GadgetData data;

data.color = ...

FrmSetGadgetData( ..., ( void * ) *( UInt32 * ) &data );

Now, I would like to retrieve this void * returned by FrmGetGadgetData
back into a GadgetData struct:

GadgetData data = FrmGetGadgetData( ... );

but I can't find the proper way to cast a void * into my GadgetData
struct.
 
J

Jim

I have 2 APIs that store/recall a void *. Since all I need to store is a
32-bit struct, I pass the actual data (instead of a pointer to it) as a
void *:

typedef
{
UInt8 color;
UInt8 index;
UInt16 resID;
} GadgetData;

GadgetData data;

data.color = ...

FrmSetGadgetData( ..., ( void * ) *( UInt32 * ) &data );

Now, I would like to retrieve this void * returned by FrmGetGadgetData
back into a GadgetData struct:

GadgetData data = FrmGetGadgetData( ... );

but I can't find the proper way to cast a void * into my GadgetData
struct.

data = *(GadgetData *)FrmGetGadgetData(...);

But I think you're making a (totally OT) mistake in the Set part
anyway.
 
T

those who know me have no need of my name

in comp.lang.c i read:
I have 2 APIs that store/recall a void *. Since all I need to store is a
32-bit struct, I pass the actual data (instead of a pointer to it) as a
void *:

serious mistake -- your code will have undefined behavior. use a union
instead.
 
S

Scott Fluhrer

This doesn't do what the OP thinks it does. This doesn't store the actual
contents of data as a 32 bit pointer, this stores a pointer to the data
structure.
data = *(GadgetData *)FrmGetGadgetData(...);

But I think you're making a (totally OT) mistake in the Set part
anyway.
Based on the OP's description (and not his code), that's not what he wants
to do. He doesn't want to dereference the pointer FrmGetGadgetData returns.
Instead, he wants to interpret the bit
pattern of that pointer as a GadgetData structure.

About the best he can do (and either involves Undefined Behavior, however,
any
method of doing what he wants involves Undefined Behavior):

void *temp = FrmGetGadgetData(...);
GadgetData data = *(GadgetData*)&temp;

or

GadgetData data;
union {
void *pointer;
GadgetData data;
} x;
x.pointer = FrmGetGadgetData(...);
data = x.data;

I don't recommend either -- instead, I suggest he rethink the idea of saving
the 32 bits as a "pointer" value.
 
C

Christian Bau

Luc Le Blanc <[email protected]> said:
I have 2 APIs that store/recall a void *. Since all I need to store is a
32-bit struct, I pass the actual data (instead of a pointer to it) as a
void *:

typedef
{
UInt8 color;
UInt8 index;
UInt16 resID;
} GadgetData;

GadgetData data;

data.color = ...

FrmSetGadgetData( ..., ( void * ) *( UInt32 * ) &data );

Now, I would like to retrieve this void * returned by FrmGetGadgetData
back into a GadgetData struct:

GadgetData data = FrmGetGadgetData( ... );

but I can't find the proper way to cast a void * into my GadgetData
struct.

The fact that you have to ask how to do this should be a strong hint
that you shouldn't do it.

Do the decent thing: void* p = malloc (sizeof GadgetData); memcpy (p,
&data); etc.
 
A

Anupam

Scott Fluhrer said:
This doesn't do what the OP thinks it does. This doesn't store the actual
contents of data as a 32 bit pointer, this stores a pointer to the data
structure.

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
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) ... which
is a 32 bit object and forcibly say that this is a void * and pass it
to the function... finally the actual data bytes are being sent as a
32 bit pointer.

Based on the OP's description (and not his code), that's not what he wants
to do. He doesn't want to dereference the pointer FrmGetGadgetData returns.
Instead, he wants to interpret the bit
pattern of that pointer as a GadgetData structure.

About the best he can do (and either involves Undefined Behavior, however,
any
method of doing what he wants involves Undefined Behavior):

void *temp = FrmGetGadgetData(...);
GadgetData data = *(GadgetData*)&temp;

Yup agreed.

Im guessing that the OP would have tried :
data = (GadgetData)(FrmGetGadgetData( ... ));
This gave a "incompatible type conversion" error . This is because
the C compiler thinks that this sort of a transformation need never be
done on the semantics of the identifier of an object.
or

GadgetData data;
union {
void *pointer;
GadgetData data;
} x;
x.pointer = FrmGetGadgetData(...);
data = x.data;

I don't recommend either -- instead, I suggest he rethink the idea of saving
the 32 bits as a "pointer" value.

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. Instead I would suggest that
the OP make his own API's .. they'd be worth the effort.
 
C

Chris Torek

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).
which is a 32 bit object

(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.
 
A

Anupam

Chris Torek said:
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*.

Absolutely, I slipped up in the phraseology there.
(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.)

Well, well this is a nice fact ... my language showed my inner sort
of feeling that pointers remain pointers ... no real manipulation is
done ... shows the dangers of the one architecture mindset.

[snip]

I absolutely agree with all the comments there.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top