问题
Here is a structure used in a program:
struct basic_block
{
void * aux;
/* Many other fields,
which are irrelevant. */
};
Now:
- Several instances of
basic_block
exist during the execution of the program. - The program works in stages/passes, which are executed one after another.
- The
aux
field is meant for storing stage andbasic_block
specific data during the execution of a stage, and freed by the stage itself (so the next stage can reuse it). This is why it is avoid *
.
My stage uses aux
to store a struct
, so each time I want to access something, I have to do a cast:
( (struct a_long_struct_name *) (bb->aux))->foo_field = foo;
Now, my problem: Casting it each time like this is a pain, and difficult to read when it is part of more complicated expressions. My proposed solution was: Use a macro to do the cast for me:
#define MY_DATA(bb) \
( (struct a_long_struct_name *) (bb)->aux)
Then I can access my data with:
MY_DATA(bb)->foo_field = foo;
But: I cannot use MY_DATA(bb)
itself as an L-value (for a malloc
), so I using that macro isn't such a good idea after all:
/* WRONG! I cannot assign to the result of a cast: */
MY_DATA(bb) = malloc (sizeof (struct a_long_struct_name));
My question:
What can I do in order to refer to aux
in a clean way and still be able to use it as an L-value.
回答1:
You can cast the address of the field to struct a_long_struct_name **
and then dereference it:
#define MY_DATA(bb) \
(* ((struct a_long_struct_name **) &(bb)->aux) )
This will work in both the constructs you have shown.
If the set of possibilities for the concrete type of aux
can be known at the point where struct basic_block
is declared, though, it would be cleaner to use a union:
struct basic_block
{
union {
struct a_long_struct_name *alsn;
/* etc */
} aux;
/* Many other fields,
which are irrelevant. */
};
and then
#define MY_DATA(bb) ((bb)->aux.alsn)
回答2:
Try:
#define MY_DATA(bb) \
( *(struct a_long_struct_name **) &(bb)->aux)
Note that this introduces aliasing so you'll have to be consistent in your use of the macro.
回答3:
It isn't necessary to litter your code with macro calls:
struct a_long_struct_name* strp = malloc(sizeof *strp);
if (!strp)
<handle OOM>
bb->aux = strp;
strp->foo_field = foo;
etc.
回答4:
You can't. C forbids accessing an object of one type (void *
) via an lvalue expression of different type (struct a_long_struct_name *
), and breaking this rule is an aliasing violation. What that means from a practical standpoint is that the compiler is allowed to assume these two lvalues cannot refer to the same memory, and thus may reorder reads and writes in a way that breaks your code badly. Formally, it's just undefined behavior, which means anything could happen.
What you should do is use the original version of your code in the question, and forget about using MY_DATA(bb)
as an lvalue. Instead just assign directly to bb->aux
. This is what void *
is for.
来源:https://stackoverflow.com/questions/11122491/l-value-trouble-when-using-a-void-as-a-generic-data-container