Unions Redux

R

Robert Gamble

Where does it say that? Is it aliasing rules or something else?
Because I was suggested here it doesn't matter whether we do

memcpy(&intval, &doubleval, sizeof intval);

or

memcpy(chararray, &doubleval, sizeof intval);
memcpy(&intval, chararray, sizeof intval);

and clearly aliasing rules do not apply to the latter. If it's indeed
aliasing rules, then so be it, it'd be a magic and it'd be fine. In
two posts I was told about trap values, which I take as a possible
explanation of why standard would opt to say something is undefined
(i.e. the reason, the rationale), this is fine. But what exactly place
in the standard says that

unsigned long longval;
unsigned int intval;
/* make sure no trap representation involved */
...
memcpy(&intval, &longval, sizeof intval);

First off, as far as I can tell, none of your examples violate the
aliasing rules. Second, if the change to the bit-representation of an
object does not result in a trap representation, using the value does
not result in undefined behavior. Determining that the result is not
a trap representation is of course not easy to do in a portable way.

Robert Gamble
 
M

muntyan

First off, as far as I can tell, none of your examples violate the
aliasing rules. Second, if the change to the bit-representation of an
object does not result in a trap representation, using the value does
not result in undefined behavior. Determining that the result is not
a trap representation is of course not easy to do in a portable way.

I posted code which *ensures* there are no problems with
representation. The memcpy() call isn't going to be executed on all
possible implementations, but it's going to be called on some (e.g.
your or mine), and if it's called then there are no problems with
representation [1].

So you are saying it's fine to copy bits from double to an int *given*
no representation problems happen. If I messed up in the last
sentence, then this one: my example with kind_of_log2 is strictly-
conforming (after you fix obvious mistake in kind_of_log2 and perhaps
other mistakes I didn't notice).

The post I replied to said "it's UB because standard explicitly says
so". I guess I am not the only one confused by this stuff.

Yevgen
 
R

Robert Gamble

Jack said:
On 14 Mar 2007 15:10:44 -0700, "Old Wolf" <[email protected]>
wrote in comp.lang.c:
Ok, we've had two long and haphazard threads about unions recently,
and I still don't feel any closer to certainty about what is permitted
and what isn't. The other thread topics were "Real Life Unions"
and "union {unsigned char u[10]; ...} ".
Most of the rambling was caused by the original OP, I think, rather
than the material. I am not criticizing, just observing. [snip]
There is no difference in aliasing in a union than there is via
pointer casting.
Sorry if it's something obvious or stupid, but please consider this
(no pointers involved).

You are mistaken, of course there are pointers involved. Just not
pointer objects.
Suppose double is eight bytes big, int is four bytes, there are
no padding bits in int.
/* (1) get bits from a double and see what happens */
double a = 3.45; unsigned int b;
memcpy(&b, &a, sizeof b);

There are two pointers used in the function call statement. The &
operator generates two addresses, which are passed to memcpy() as
pointers to void.
printf("%u", b);

The behavior is undefined.
/* (2) do same thing using a union */
union U {double a; unsigned int b;} u;
u.a = 3.14;
printf("%u", u.b);

The behavior is undefined.
/* (3) initialize union with memcpy and access its member */
double d;
union U {double a; unsigned int b;} u;
d = 3.14;
memcpy(&u, &d, sizeof d);
printf("%u", u.b);

The behavior is undefined.
Which of three are valid? I think (1) is; (3) maybe; (2) maybe, if
(3) valid and aliasing rules don't work here. If aliasing rules
do apply to (2), then how is first assignment in (2) different
from memcpy() in (3)?

None of the three are valid. The standard does not give you
permission to access an lvalue of type unsigned int after writing some
or all of the bits of a double to it.

C&V please.

Robert Gamble
 
O

Old Wolf

Where does it say that?

C99 6.5#6 and 6.5#7 .
Is it aliasing rules
Yes

Because I was suggested here it doesn't matter whether we do

memcpy(&intval, &doubleval, sizeof intval);
or
memcpy(chararray, &doubleval, sizeof intval);
memcpy(&intval, chararray, sizeof intval);

Those two both have the same effect, if chararray is big enough.
and clearly aliasing rules do not apply to the latter.

Actually they do. (See below)
But what exactly place in the standard says that

unsigned long longval;
unsigned int intval;
/* make sure no trap representation involved */
...
memcpy(&intval, &longval, sizeof intval);

is undefined?

It isn't undefined. The behaviour is only undefined if you
subsequently
evaluate the value of 'intval'. The reason it is undefined at that
point
is because of the aliasing rules: you are using an 'unsigned int'
lvalue
to access a bit pattern that is (part of) the bit pattern of a
different
type (namely, unsigned long).

'trap representation' also does not come into it unless you evaluate
intval. Memory is just memory, you can write whatever crap you want
into it. Problems only arise when you try and treat said crap as if
you are expecting a valid value.
 
R

Robert Gamble

C99 6.5#6 and 6.5#7 .



Those two both have the same effect, if chararray is big enough.


Actually they do. (See below)




It isn't undefined. The behaviour is only undefined if you
subsequently
evaluate the value of 'intval'. The reason it is undefined at that
point
is because of the aliasing rules: you are using an 'unsigned int'
lvalue
to access a bit pattern that is (part of) the bit pattern of a
different
type (namely, unsigned long).

6.5p6 starts out:
"An object shall have its stored value accessed only by an lvalue
expression that has one of
the following types:"

"access" is defined as "to read or modify the value of an object".
According to your logic, the following would invoke undefined
behavior:

union {int i; float f;} u;
u.f = 1.2;
u.i = 10;

That seems pretty silly.

Robert Gamble
 
M

muntyan

C99 6.5#6 and 6.5#7 .

6.5p6 only says that after 'unsigned b;' effective type of b is
unsigned.

It's not obviouos that 6.5p7 talks about any access to the value at
any time
(access as in "look at what is *or was* inside the object"). Say, we
can take a
double, copy it to a character array, print bits to the screen, ask
user to enter
them back, and then set individual bits in an unsigned value [1]. We
*can* set any
bits we like in an unsigned, it's just arithmetic operations.

If character array here is important and aliasing rules stop working
after copying
into a character array, then it contradicts to what you said below. If
aliasing
rules stop after we ask user to enter value, then it's *magic* (we
could write
sequence of 0 and 1 characters to a file for that matter).
Those two both have the same effect, if chararray is big enough.

Yevgen

[1] This is not necessarily some simple arithmetic operation like
uint_val = uchar_array[0] + (1u<<CHAR_BIT)*uchar_array[0] + ...
of course; but we can get the mapping from unsigned value bits to bits
in unsigned char array by simply looking at the result of
unsigned foo = 1u << n;
memcpy(&char_array, &foo, sizeof foo);
It assumes absence of padding bits (which we *may* assume).
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,223
Latest member
Jurgen2087

Latest Threads

Top