[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Another Union question

Quentin Pope

9/6/2011 9:01:00 PM

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)?
4 Answers

James Kuyper

9/6/2011 9:44:00 PM

0

On 09/06/2011 05:01 PM, Quentin Pope wrote:
> 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.

Ben Bacarisse

9/7/2011 1:09:00 AM

0

James Kuyper <jameskuyper@verizon.net> writes:

> On 09/06/2011 05:01 PM, Quentin Pope wrote:
<snip>
>> Now I have an union
>> union r32{
>> uint8 l;
>> uint16 x;
>> uint32 val;
<snip>
>> 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.

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>
--
Ben.

Keith Thompson

9/7/2011 1:33:00 AM

0

James Kuyper <jameskuyper@verizon.net> writes:
> 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);

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

James Kuyper

9/7/2011 12:04:00 PM

0

On 09/06/2011 09:08 PM, Ben Bacarisse wrote:
> James Kuyper <jameskuyper@verizon.net> writes:
>
>> On 09/06/2011 05:01 PM, Quentin Pope wrote:
> <snip>
>>> Now I have an union
>>> union r32{
>>> uint8 l;
>>> uint16 x;
>>> uint32 val;
> <snip>
>>> 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.
>
> 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);

--
James Kuyper