Another Union question

Q

Quentin Pope

We know that &struct is equiv to &struct.firstMember. So far so good.

Now I have an union
union r32{
uint8 l;
uint16 x;
uint32 val;

};

union r32 reg;

and a function
void f(union r32* a){a->x=1;}

then

is to do f(&reg.x) or f(&reg.l) or f(&reg.val) OK or not?
is it &reg.x=&reg.l=&reg.val ?

Or I have to do f(&reg)?
 
J

James Kuyper

We know that &struct is equiv to &struct.firstMember. So far so good.

Not true. Those two pointers have different types, and correspondingly
different behavior when dereferenced, so they are not equivalent. A more
accurate statement would be

(firstmember_type*)&struct == &struct.firstMember

and also:

&struct == (struct struct_tag*)&struct.firstMember

Note: if firstMember is a bit-field, &struct.firstMember is a constraint
violation, and these guarantees don't hold.
Now I have an union
union r32{
uint8 l;
uint16 x;
uint32 val;

If you're using C99, you should #include <stdint.h> and replace these
with uint8_t, uint16_t, uint32_t. These types are not guaranteed to be
supported, though they should in fact be supported on almost all
implementations. If a serious C99 implementation does not provide those
types on given platform, you can reasonably assume that it's extremely
difficult, perhaps even impossible, to provide those types on that
platform. You can check whether or not they are supported by

#if !defined(UINT8_MAX) || !defined(UINT16_MAX) || !defined(UINT32_MAX)
#error uint8_t, uint16_t or uint32_t not supported.
#endif
};

union r32 reg;

and a function
void f(union r32* a){a->x=1;}

then

is to do f(&reg.x) or f(&reg.l) or f(&reg.val) OK or not?

With a function prototype in scope, all of those pointers are implicitly
converted to union r32* when passed to that function. The result of that
conversion is guaranteed to point at the containing union, so all three
are OK.
is it &reg.x=&reg.l=&reg.val ?

No, but
(union r32*)&reg.x == (union r32*)&reg.l

and similarly for the other combinations. It's also true that
(uint32*)(union r32*)&reg.x == &reg.val

It would seem obvious that this means that

(uint32*)&reg.x == &reg.val

and this is indeed true on (almost?) all implementations of C. However,
the standard does not actually guarantee that (uint32*)(union r32*)
produces the same result as (uint32*).
Or I have to do f(&reg)?

While you do not have to use &reg, I would strongly recommend it unless
you have a compelling need to use something else.
 
B

Ben Bacarisse

James Kuyper said:
On 09/06/2011 05:01 PM, Quentin Pope wrote:

With a function prototype in scope, all of those pointers are implicitly
converted to union r32* when passed to that function. The result of that
conversion is guaranteed to point at the containing union, so all three
are OK.

That's not right. It's a constraint violation if an argument has a type
that means it could not be assigned to an object with the (unqualified)
version of the parameter type.

Because it's a constraint, there must be a diagnostic and the program
will have undefined behaviour. Many implementation take that as
permission to go on and translate the unit as if a cast had been used to
specify the conversion, but that's not in any way required or
guaranteed.

<snip>
 
K

Keith Thompson

James Kuyper said:
On 09/06/2011 05:01 PM, Quentin Pope wrote: [...]
Now I have an union
union r32{
uint8 l;
uint16 x;
uint32 val; [...]
};

union r32 reg;

and a function
void f(union r32* a){a->x=1;}

then

is to do f(&reg.x) or f(&reg.l) or f(&reg.val) OK or not?

With a function prototype in scope, all of those pointers are implicitly
converted to union r32* when passed to that function. The result of that
conversion is guaranteed to point at the containing union, so all three
are OK.
[...]

There is no implicit conversion from a pointer-to-integer type to a
pointer-to-union type. An explicit conversion (a cast) should work ok:

f((union r32*)&reg.x);
f((union r32*)&reg.x);
f((union r32*)&reg.val);
 
J

James Kuyper

That's not right. It's a constraint violation if an argument has a type
that means it could not be assigned to an object with the (unqualified)
version of the parameter type.

You're right - I don't know how I managed to forget that. It is
therefore not merely pointless, but just plain wrong, to use any of
those expressions.

f((union r32*)&reg.x) would, however, be a perfectly fine, though
pointlessly complicated, way of writing f(&reg). If taken as short-hand
for something that happens in two different stages in widely separated
scopes, it actually becomes more reasonable:

void g(uint16 *pui16)
{
f((union r32*)pui16));
}

In some other function:

g(&reg.x);
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top