Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > [union] Pointers to inherited structs are valid ?

Reply
Thread Tools

[union] Pointers to inherited structs are valid ?

 
 
Shao Miller
Guest
Posts: n/a
 
      01-04-2013
On 1/3/2013 20:55, Shao Miller wrote:
>
> I absolutely agree with your equivalence between 'memcpy' and union
> members. Also: Re-interpreting the object representation with something
> like:
>
> A * ptr;
> (*((B **) &ptr));
>
> (where types 'A' and 'B' have the same representation.)
>


I meant where 'A *' and 'B *' have the same representation.

 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      01-04-2013
On 1/3/2013 21:00, Shao Miller wrote:
> On 1/3/2013 20:55, Shao Miller wrote:
>>
>> I absolutely agree with your equivalence between 'memcpy' and union
>> members. Also: Re-interpreting the object representation with something
>> like:
>>
>> A * ptr;
>> (*((B **) &ptr));
>>
>> (where types 'A' and 'B' have the same representation.)
>>

>
> I meant where 'A *' and 'B *' have the same representation.
>


(And alignment requirements.)

However, please allow me to retract this equivalence with type-punning
via union members and 'memcpy'. After reviewing some discussion with
Mr. Clive Feather, now I'm not sure so... He points out that there is
an effective type involved, but we end up with an lvalue attempting to
access a stored value with that effective type associated, but the
lvalue attempting to access it has a type not permitted by 6.5p7.

- Shao Miller
 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      01-04-2013
On 1/3/2013 20:55, Shao Miller wrote:
> On 1/3/2013 18:31, Tim Rentsch wrote:
>> Shao Miller <(E-Mail Removed)> writes:
>>
>>> On 1/2/2013 13:45, Tim Rentsch wrote:
>>>> Shao Miller <(E-Mail Removed)> writes:
>>>>> But the implementation's actual pointer representation could be
>>>>> complicated and so there's still no guarantee. If you can dream up a
>>>>> pointer representation, then you can dream up a counter-example to
>>>>> your code's portability.
>>>>
>>>> The stipulation that all pointers to structs have the same
>>>> representation and alignment requirements means that the
>>>> type-punning union member access has to work. That's what
>>>> having the same represention means -- that the same object
>>>> representation (ie, the same bytes) will have the same value.
>>>> Any choice of representations for the two cases that doesn't
>>>> produce identical results here means the two representations
>>>> are not the same, ie, the implementation is not conforming
>>>> (under C99/C11 rules).
>>>>
>>>
>>> Here are three examples that I would consider to be counter-examples:
>>>
>>> 1. A 'struct any *' pointer representation that is a simple index.
>>>
>>> This could provide a level of indirection into a table. The table
>>> element could have type and bounds information, along with some other
>>> form of address for the pointee. When the representation (a simple
>>> index) is loaded into a 'struct bar *' instead of into a 'struct foo
>>> *', a trap could be generated.
>>>
>>> 2. A 'struct any *' pointer representation that encodes bounds
>>> information. While the original post "has this covered" because the
>>> bounds of the the original pointee encompass the bounds of the members
>>> and sub-members, it's not safe in the general case. When the
>>> representation is loaded into a 'struct bigger *' instead of a 'struct
>>> smaller *', the bounds mismatch could generate a trap.
>>>
>>> 3. A 'struct any *' pointer representation that encodes type
>>> information. Maybe for the sole reason of generating a trap when the
>>> representation is loaded into an incompatible pointer type of object.

>>
>> These ideas aren't consistent with how the Standard uses the
>> notion of having the same representation in other instances. For
>> example, an object of type (int) has the same representation and
>> alignment requirements as an object of type (const int). Yet it's
>> ridiculous to think that loading an (int) object through a pointer
>> of type (const int *) might cause a trap when accessing the object
>> just as a plain int wouldn't, despite the two types being distinct
>> and not compatible.
>>

>
> Ok. I agree with your example. But 2 points:
>
> - The representation of 'int' is discussed in much greater detail than
> the representation of any pointer type. Pointer representations are
> much more opaque and free for the implementation to decide upon.
>
> - I don't think it makes practical sense to encode type information in
> the padding bits of an 'int', but it certainly seems useful to encode
> extra information in a pointer representation, since they are derived
> types with abstract values.
>
> Surely if, in
>
> void somefunc(void) {
> unsigned char c;
> /* ... */
> }
>
> 'c' is permitted to have a trap representation due to its "provenance,"
> then it is especially convenient that pointer representations are
> opaque, so "provenance" or other meta-data can be encoded directly. No?
>
>>> It seems clear to me that size, alignment, argument promotion (none)
>>> and format of 'struct foo *' and 'struct bar *' must be the same, but
>>> I don't yet understand how that ties into compatible types nor into
>>> defined behaviour, since
>>>
>>> "Certain object representations need not represent a value of the
>>> object type. If the stored value of an object has such a
>>> representation and is read by an lvalue expression that does not have
>>> character type, the behavior is undefined. If such a representation is
>>> produced by a side effect that modifies all or any part of the object
>>> by an lvalue expression that does not have character type, the
>>> behavior is undefined.41) Such a representation is called a trap
>>> representation."
>>>
>>> Why can a valid 'struct foo *' value's representation represent a
>>> valid 'struct foo *' value but not a trap for a 'struct bar *'? For
>>> example, it might be useful to trap a 'const struct baz *'
>>> representation read into a 'struct baz *' object. A single bit in the
>>> representation would be sufficient for that. The representation would
>>> be the same, wouldn't it?

>>
>> No. I expect you're thinking of "representation" as more or less
>> synonymous with "format",

>
> Yes, you are right about that.
>
>> but representation means more than that.
>> The representation of a type is the mapping from the bits (ie, the
>> byte values of the object representation) to values in the type's
>> abstract value space, including trap values. If two types have
>> the same representation, that means the two mappings produce
>> corresponding values (ie, for each object representatioon) in the
>> two abstract value spaces. For C, corresponding values are what
>> would be produced by conversion between the two types in question.
>> In other words, if types A and B have the same representation,
>> then copying the bytes (eg, with memcpy()) from an 'A a;' into a
>> 'B b;' must give the same results as 'b = (B) a;'. Any change in
>> behavior between the two cases means the two representations are
>> not the same.

>
> I'm struggling to reconcile that with C99's 3.17p1 and 6.2.6.1. 3.17p1:
>
> "value
> precise meaning of the contents of an object when interpreted as
> having a specific type"
>
> I'm missing the part where it's possible for the same object
> representation to represent the same value for two incompatible types,
> since the value depends on the type.
>
> Regarding conversion, 6.3p2 has that
>
> "Conversion of an operand value to a compatible type causes no change
> to the value or the representation."
>
> Why mention both of them instead of simply "representation," if there's
> a one-to-one correspondence between representation and value, given
> compatible type? (Let alone incompatible types with the same
> representation.)
>
> Regarding pointer conversion, 6.3.2.3p1 has that
>
> "For any qualifier q, a pointer to a non-q-qualified type may be
> converted to a pointer to the q-qualified version of the type; the
> values stored in the original and converted pointers shall compare equal."
>
> Doesn't this explicitly hint that a 'const int *' value's representation
> is permitted to be a trap representation for an 'int *', but not the
> other way around? It seems convenient that such meta-data can be
> directly encoded into the pointer representation, since pointer
> representation is so opaque.
>
> There's also p7:
>
> "A pointer to an object or incomplete type may be converted to a
> pointer to a different object or incomplete type. If the resulting
> pointer is not correctly aligned57) for the pointed-to type, the
> behavior is undefined. Otherwise, when converted back again, the result
> shall compare equal to the original pointer. ..."
>
> Doesn't this explicitly hint that it's not the most portable idea to do
> anything much with a converted pointer other than to eventually convert
> it back before using it? If I understand you correctly, there's no
> conversion happening, as the value is simply becoming one in a different
> type's value space, so there's no problem with p7.
>
> Regarding your equivalence between the 'memcpy' and the cast for two
> types with the same representation, 6.5.4p4 has that
>
> "Preceding an expression by a parenthesized type name converts the
> value of the expression to the named type. This construction is called a
> cast.89) A cast that specifies no conversion has no effect on the type
> or value of an expression."
>
> If '(B) a' is already the same value as 'a' due to the types having the
> same representation, then there is no conversion, right? If that's the
> case, then the type of '(B) a' should be 'A'. Like 3.17p1, type and
> value are once again tied together, so it seems to me that incompatible
> types can have incompatible values.
>
> HOWEVER, you said _corresponding_values_. So I'd ask: May a value in
> the value space for type 'A' not have a corresponding, but invalid value
> in the value space for type 'B'? If it may, then I fail to understand
> why the original post's code is well-defined in C99 and C11.
>
>> Accessing via type B using a union member access
>> works the same way that the memcpy() would.

>
> I absolutely agree with your equivalence between 'memcpy' and union
> members. Also: Re-interpreting the object representation with something
> like:
>
> A * ptr;
> (*((B **) &ptr));
>
> (where types 'A' and 'B' have the same representation.)
>
>> For pointers, there is the additional concern that the converted
>> or corresponding value be a non-trap value in the abstract value
>> space of the new pointer type. However, in the particular example
>> here (ie, in the original posting, even though since disappeared
>> in the subthread), we know the pointer conversions have to work
>> because of the way the particular structs being pointed to are
>> nested.
>>

>
> Ah, that answers my last question, above. But there's a bit of a jump
> in the logic that I can't grasp, and that's why the nesting of the
> structures in the original example has anything at all to do with the
> corresponding pointer value having to work. Yes, I agree that the
> original example's bounds are covered because of the nesting, but I
> don't understand why that's the only important subject.
>
> To back up a bit from the original example, 'char *' and 'void *' have
> the same representation. Would you say that in:
>
>
> void reinterpret(void) {
> void * vp = &vp;
> vp = (*((char **) &vp)) + 1;
> }
>


Since else-thread I'm retracting the union member type-punning
equivalence with this kind of raw re-interpretation, please allow me to
also retract this example and replace it with:

void reinterpret(void) {
union {
void * vp;
char * cp;
} u = { &u };
u.cp = u.cp + 1;
}

> the expression-statement has Standard-defined behaviour? I'm worried
> about this example because an implementation might wish to represent
> "the stride" of the pointer arithmetic, just as "Multi-Dimensional Array
> Simulator"[1] does. Implicit and explicit conversions (like the
> promotions, casts, equality and ternary semantics, etc.) seem to offer
> all the protection we need, while re-interpretation does not.
>
> [1] http://www.iso-9899.info/wiki/Code_snippets


 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-07-2013
Shao Miller <(E-Mail Removed)> writes:

> On 1/3/2013 18:31, Tim Rentsch wrote:
>> Shao Miller <(E-Mail Removed)> writes:
>>
>>> On 1/2/2013 13:45, Tim Rentsch wrote:
>>>> Shao Miller <(E-Mail Removed)> writes:
>>>>> But the implementation's actual pointer representation could be
>>>>> complicated and so there's still no guarantee. If you can dream up a
>>>>> pointer representation, then you can dream up a counter-example to
>>>>> your code's portability.
>>>>
>>>> The stipulation that all pointers to structs have the same
>>>> representation and alignment requirements means that the
>>>> type-punning union member access has to work. That's what
>>>> having the same represention means -- that the same object
>>>> representation (ie, the same bytes) will have the same value.
>>>> Any choice of representations for the two cases that doesn't
>>>> produce identical results here means the two representations
>>>> are not the same, ie, the implementation is not conforming
>>>> (under C99/C11 rules).
>>>>
>>>
>>> Here are three examples that I would consider to be counter-examples:
>>>
>>> 1. A 'struct any *' pointer representation that is a simple index.
>>>
>>> This could provide a level of indirection into a table. The table
>>> element could have type and bounds information, along with some other
>>> form of address for the pointee. When the representation (a simple
>>> index) is loaded into a 'struct bar *' instead of into a 'struct foo
>>> *', a trap could be generated.
>>>
>>> 2. A 'struct any *' pointer representation that encodes bounds
>>> information. While the original post "has this covered" because the
>>> bounds of the the original pointee encompass the bounds of the members
>>> and sub-members, it's not safe in the general case. When the
>>> representation is loaded into a 'struct bigger *' instead of a 'struct
>>> smaller *', the bounds mismatch could generate a trap.
>>>
>>> 3. A 'struct any *' pointer representation that encodes type
>>> information. Maybe for the sole reason of generating a trap when the
>>> representation is loaded into an incompatible pointer type of object.

>>
>> These ideas aren't consistent with how the Standard uses the
>> notion of having the same representation in other instances. For
>> example, an object of type (int) has the same representation and
>> alignment requirements as an object of type (const int). Yet it's
>> ridiculous to think that loading an (int) object through a pointer
>> of type (const int *) might cause a trap when accessing the object
>> just as a plain int wouldn't, despite the two types being distinct
>> and not compatible.

>
> Ok. I agree with your example. But 2 points:
>
> - The representation of 'int' is discussed in much greater
> detail than the representation of any pointer type. Pointer
> representations are much more opaque and free for the
> implementation to decide upon.


That doesn't change the point I was making.

> - I don't think it makes practical sense to encode type
> information in the padding bits of an 'int', but it certainly
> seems useful to encode extra information in a pointer
> representation, since they are derived types with abstract
> values.


Even if that's true, it doesn't change what the Standard mandates.

> Surely if, in
>
> void somefunc(void) {
> unsigned char c;
> /* ... */
> }
>
> 'c' is permitted to have a trap representation due to its
> "provenance,"


It isn't. You are either mis-remembering or have misunderstood.

> then it is especially convenient that pointer
> representations are opaque, so "provenance" or other meta-data can be
> encoded directly. No?


Irrelevant. Such a statement might be an argument for changing
a future Standard, but it has no bearing on what is said
in the current Standard.

>>> It seems clear to me that size, alignment, argument promotion (none)
>>> and format of 'struct foo *' and 'struct bar *' must be the same, but
>>> I don't yet understand how that ties into compatible types nor into
>>> defined behaviour, since
>>>
>>> "Certain object representations need not represent a value of the
>>> object type. If the stored value of an object has such a
>>> representation and is read by an lvalue expression that does not have
>>> character type, the behavior is undefined. If such a representation is
>>> produced by a side effect that modifies all or any part of the object
>>> by an lvalue expression that does not have character type, the
>>> behavior is undefined.41) Such a representation is called a trap
>>> representation."
>>>
>>> Why can a valid 'struct foo *' value's representation represent a
>>> valid 'struct foo *' value but not a trap for a 'struct bar *'? For
>>> example, it might be useful to trap a 'const struct baz *'
>>> representation read into a 'struct baz *' object. A single bit in the
>>> representation would be sufficient for that. The representation would
>>> be the same, wouldn't it?

>>
>> No. I expect you're thinking of "representation" as more or less
>> synonymous with "format",

>
> Yes, you are right about that.
>
>> but representation means more than that.
>> The representation of a type is the mapping from the bits (ie, the
>> byte values of the object representation) to values in the type's
>> abstract value space, including trap values. If two types have
>> the same representation, that means the two mappings produce
>> corresponding values (ie, for each object representatioon) in the
>> two abstract value spaces. For C, corresponding values are what
>> would be produced by conversion between the two types in question.
>> In other words, if types A and B have the same representation,
>> then copying the bytes (eg, with memcpy()) from an 'A a;' into a
>> 'B b;' must give the same results as 'b = (B) a;'. Any change in
>> behavior between the two cases means the two representations are
>> not the same.

>
> I'm struggling to reconcile that with C99's 3.17p1 and 6.2.6.1.
> [quoted paragraph snipped]
>
> I'm missing the part where it's possible for the same object
> representation to represent the same value for two incompatible
> types, since the value depends on the type.


I don't see why you are confused. There is no wording that
forbids it, and it's obviously possible, as 'int' and 'const int'
illustrate. On many machines 'int' and 'long' provide another
example. Or two of the three character types.

> Regarding conversion, 6.3p2 has that
>
> "Conversion of an operand value to a compatible type causes no
> change to the value or the representation."
>
> Why mention both of them instead of simply "representation," if
> there's a one-to-one correspondence between representation and
> value, given compatible type? (Let alone incompatible types
> with the same representation.)


Do you think the Standard includes a sentence saying compatible
types must have the same representation and alignment requirements?

Incidentally, there isn't a one-to-one correspondence between object
representations and values (necessarily, that is). The mapping is
_from_ object representations _to_ the abstract value space, but it
need not be one-to-one; also, the abstract value space includes
"trap values" which correspond to trap representations but are not
'values' as the Standard normally uses the term.

> Regarding pointer conversion, 6.3.2.3p1 has that
>
> "For any qualifier q, a pointer to a non-q-qualified type may be
> converted to a pointer to the q-qualified version of the type; the
> values stored in the original and converted pointers shall compare
> equal."
>
> Doesn't this explicitly hint that a 'const int *' value's
> representation is permitted to be a trap representation for an 'int
> *', but not the other way around? [snip]


No. Converting a valid 'const int *' to an 'int *' is well-defined
and must succeed.

> There's also p7:
>
> "A pointer to an object or incomplete type may be converted to a
> pointer to a different object or incomplete type. If the resulting
> pointer is not correctly aligned57) for the pointed-to type, the
> behavior is undefined. Otherwise, when converted back again, the
> result shall compare equal to the original pointer. ..."
>
> Doesn't this explicitly hint that it's not the most portable idea to
> do anything much with a converted pointer other than to eventually
> convert it back before using it?


No.

> If I understand you correctly, there's no conversion happening,
> as the value is simply becoming one in a different type's value
> space, so there's no problem with p7.


What I think you mean is there is no change to the object
representation (which I didn't say and which doesn't have to
be true). What I said was basically that the result must be the
same whether the object representation changes or not (in cases
where the two types involved have the same representation).

> Regarding your equivalence between the 'memcpy' and the cast for two
> types with the same representation, 6.5.4p4 has that
>
> "Preceding an expression by a parenthesized type name converts the
> value of the expression to the named type. This construction is called
> a cast.89) A cast that specifies no conversion has no effect on the
> type or value of an expression."
>
> If '(B) a' is already the same value as 'a' due to the types having
> the same representation, then there is no conversion, right?


Wrong. Casting always does a conversion, even if the conversion
doesn't change either the value or the object representation.
Assignment also always does a conversion, even if the types are
the same. Furthermore for the case we are discussing, namely two
pointer-to-structure types, if the referenced types are different
then the value spaces of the two pointer types are disjoint, so
it can't be the case that the two values are the same.

> If that's the case, then the type of '(B) a' should be 'A'.
> Like 3.17p1, type and value are once again tied together, so it
> seems to me that incompatible types can have incompatible
> values.


This sentence is gibberish.

> HOWEVER, you said _corresponding_values_. So I'd ask: May a
> value in the value space for type 'A' not have a corresponding,
> but invalid value in the value space for type 'B'? If it may,
> then I fail to understand why the original post's code is
> well-defined in C99 and C11.


I shouldn't have to explain this again. Converting the value
with a cast has to work, because of how the struct's are nested.
Therefore reinterpreting the object representation using a union
member access has to work, because that's what "having the same
representation" means.

>> Accessing via type B using a union member access
>> works the same way that the memcpy() would.

>
> I absolutely agree with your equivalence between 'memcpy' and union
> members. Also: Re-interpreting the object representation with
> something like:
>
> A * ptr;
> (*((B **) &ptr));
>
> (where types 'A' and 'B' have the same representation.)


That doesn't work, as I think you pointed out subsequently,
because of effective type rules. Except for that, yes, same
idea.

>> For pointers, there is the additional concern that the converted
>> or corresponding value be a non-trap value in the abstract value
>> space of the new pointer type. However, in the particular example
>> here (ie, in the original posting, even though since disappeared
>> in the subthread), we know the pointer conversions have to work
>> because of the way the particular structs being pointed to are
>> nested.

>
> Ah, that answers my last question, above. But there's a bit of
> a jump in the logic that I can't grasp, and that's why the
> nesting of the structures in the original example has anything
> at all to do with the corresponding pointer value having to
> work. Yes, I agree that the original example's bounds are
> covered because of the nesting, but I don't understand why
> that's the only important subject.


There are two important facts: one, the struct values are
nested appropriately; and two, the pointers to those structs
have the same representation (and alignment requirements).
Therefore the type-punning union member access gets a set
of bits that are both interpreted correctly and valid for
the type in question.

> To back up a bit from the original example, 'char *' and 'void
> *' have the same representation. Would you say that in:
>
>
> void reinterpret(void) {
> void * vp = &vp;
> vp = (*((char **) &vp)) + 1;
> }
>


Again, there is a violation of effective type rules in this case,
but if the analogous thing were done using union member access
then yes it has to work.

> the expression-statement has Standard-defined behaviour? I'm
> worried about this example because an implementation might wish
> to represent "the stride" of the pointer arithmetic, just as
> "Multi-Dimensional Array Simulator"[1] does. Implicit and
> explicit conversions (like the promotions, casts, equality and
> ternary semantics, etc.) seem to offer all the protection we
> need, while re-interpretation does not.


You're confusing what you think might be a good idea with
what the Standard mandates. My comments are concerned only
with the latter.
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-07-2013
On 1/6/2013 23:00, Tim Rentsch wrote:
> Shao Miller <(E-Mail Removed)> writes:
>> To back up a bit from the original example, 'char *' and 'void
>> *' have the same representation. Would you say that in:
>>
>> void reinterpret(void) {
>> void * vp = &vp;
>> vp = (*((char **) &vp)) + 1;
>> }

>
> Again, there is a violation of effective type rules in this case,
> but if the analogous thing were done using union member access
> then yes it has to work.
>


And here is the analogous thing, offered elsethread:

void reinterpret(void) {
union {
void * vp;
char * cp;
} u = { &u };
u.cp = u.cp + 1;
/* Hmm ^^^^ */
}

The 'u.cp' expression marked by the comment (having type 'char *') is an
lvalue whose type is not one of those listed by 6.5p7, but it attempts
to access the value of 'u.vp'. (Doesn't it?) This appears to yield
undefined behaviour, doesn't it? Or would you suggest that the 'u'
sub-expression (having the union type) is the lvalue for purposes of
6.5p7, and that the type of the containing expression 'u.cp' doesn't matter?

- Shao Miller
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-07-2013
Shao Miller <(E-Mail Removed)> writes:

> On 1/6/2013 23:00, Tim Rentsch wrote:
>> Shao Miller <(E-Mail Removed)> writes:
>>> To back up a bit from the original example, 'char *' and 'void
>>> *' have the same representation. Would you say that in:
>>>
>>> void reinterpret(void) {
>>> void * vp = &vp;
>>> vp = (*((char **) &vp)) + 1;
>>> }

>>
>> Again, there is a violation of effective type rules in this case,
>> but if the analogous thing were done using union member access
>> then yes it has to work.
>>

>
> And here is the analogous thing, offered elsethread:
>
> void reinterpret(void) {
> union {
> void * vp;
> char * cp;
> } u = { &u };
> u.cp = u.cp + 1;
> /* Hmm ^^^^ */
> }
>
> The 'u.cp' expression marked by the comment (having type 'char *') is
> an lvalue whose type is not one of those listed by 6.5p7, but it
> attempts to access the value of 'u.vp'. (Doesn't it?) This appears
> to yield undefined behaviour, doesn't it? Or would you suggest that
> the 'u' sub-expression (having the union type) is the lvalue for
> purposes of 6.5p7, and that the type of the containing expression
> u.cp' doesn't matter?


Look harder. Think more. Write less.
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-07-2013
On 1/7/2013 02:06, Tim Rentsch wrote:
> Shao Miller <(E-Mail Removed)> writes:
>
>> On 1/6/2013 23:00, Tim Rentsch wrote:
>>> Shao Miller <(E-Mail Removed)> writes:
>>>> To back up a bit from the original example, 'char *' and 'void
>>>> *' have the same representation. Would you say that in:
>>>>
>>>> void reinterpret(void) {
>>>> void * vp = &vp;
>>>> vp = (*((char **) &vp)) + 1;
>>>> }
>>>
>>> Again, there is a violation of effective type rules in this case,
>>> but if the analogous thing were done using union member access
>>> then yes it has to work.
>>>

>>
>> And here is the analogous thing, offered elsethread:
>>
>> void reinterpret(void) {
>> union {
>> void * vp;
>> char * cp;
>> } u = { &u };
>> u.cp = u.cp + 1;
>> /* Hmm ^^^^ */
>> }
>>
>> The 'u.cp' expression marked by the comment (having type 'char *') is
>> an lvalue whose type is not one of those listed by 6.5p7, but it
>> attempts to access the value of 'u.vp'. (Doesn't it?) This appears
>> to yield undefined behaviour, doesn't it? Or would you suggest that
>> the 'u' sub-expression (having the union type) is the lvalue for
>> purposes of 6.5p7, and that the type of the containing expression
>> u.cp' doesn't matter?

>
> Look harder. Think more. Write less.
>


Please don't resort to this sort of personally-directed nonsense as
you've done before. If you don't have an answer, please simply say so.
If you really think I've missed something, it'd certainly be more
helpful to point it out instead of implying laziness or stupidity.

If you think I write too much, well, I think you write too little
Standard, and too much "Mr. T. Rentsch knows best." Unfortunately, that
doesn't work for me, as your knowledge isn't directly accessible to me.
I'm sorry if that makes our discussions difficult! If you choose to
help me to understand your valuable perspective, I'll be appreciative.

Just in case you're nit-picking an error in the code that hardly seems
relevant to the meat of the question, please allow me to offer the
corrected code:

void reinterpret(void) {
union {
void * vp;
char * cp;
} u;
u.vp = &u;
u.cp = u.cp + 1;
/* Hmm ^^^^ */
}

int main(void) {
reinterpret();
return 0;
}

Otherwise, would anyone else please point out what I might've missed
about whether or not the above example results in undefined behaviour?
The "shall"[6.5p7] is outside of a constraint, so that'd seem to be
undefined behaviour if the lvalue under consideration is 'u.cp'. If the
lvalue is 'u', then its union type _is_ permitted by 6.5p7 (as
acknowledged in a previous post, above), but it'd be good to know
_which_ is the lvalue under consideration.

- Shao Miller
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-07-2013
On 1/6/2013 23:00, Tim Rentsch wrote:
> Shao Miller <(E-Mail Removed)> writes:
>> Surely if, in
>>
>> void somefunc(void) {
>> unsigned char c;
>> /* ... */
>> }
>>
>> 'c' is permitted to have a trap representation due to its
>> "provenance,"

>
> It isn't. You are either mis-remembering or have misunderstood.
>


Committee Discussion in Defect Report #260:

"In addition the C Standard does not prohibit an implementation from
tracking the provenance of the bit-pattern representing a value. An
indeterminate value happening to have a bit pattern that is identical to
a bit pattern representing a determinate value is not sufficient to
allow access to the indeterminate value free from undefined behavior."

That suggests to me that real implementation representatives discussed
it, and some of them must have argued that there is more to object
representation and value than a simple mapping. I suggest that there
are other meta-considerations (such as "indeterminate value"), some of
which are crucial to an implementation that wishes to have "enforceable
coding rules":

http://www.open-std.org/jtc1/sc22/WG...docs/n1663.pdf

'c' above is permitted to have a trap representation without even having
that fact coded into its object representation. If I've misunderstood,
then I apologize. If you have further knowledge of the status of DR
#260, then please share!

- Shao Miller
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-07-2013
On 1/7/2013 04:03, Shao Miller wrote:
>
> void reinterpret(void) {
> union {
> void * vp;
> char * cp;
> } u;
> u.vp = &u;
> u.cp = u.cp + 1;
> /* Hmm ^^^^ */
> }
>
> int main(void) {
> reinterpret();
> return 0;
> }
>
> Otherwise, would anyone else please point out what I might've missed
> about whether or not the above example results in undefined behaviour?
> The "shall"[6.5p7] is outside of a constraint, so that'd seem to be
> undefined behaviour if the lvalue under consideration is 'u.cp'. If the
> lvalue is 'u', then its union type _is_ permitted by 6.5p7 (as
> acknowledged in a previous post, above), but it'd be good to know
> _which_ is the lvalue under consideration.


Mr. Clive D. W. Feather very kindly gave his valuable time and shared in
agreement about this code.

6.5p7 makes this undefined behaviour, just as it does for the original
post's use of the two different union members, despite the two
pointer-to-structure types having the same representation and alignment
requirements.

The penultimate bullet of 6.5p7 regarding unions is so that the
following code is well-defined:

void reinterpret(void) {
union {
void * vp;
char * cp;
} u, v;
u.vp = &u;

/* Union lvalue on right accesses the stored value */
v = u;
(void) v;
}

int main(void) {
reinterpret();
return 0;
}

I'm glad that if I've lost some marbles, someone else lost the same ones.

- Shao Miller
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-12-2013
Shao Miller <(E-Mail Removed)> writes:

> On 1/6/2013 23:00, Tim Rentsch wrote:
>> Shao Miller <(E-Mail Removed)> writes:
>>> Surely if, in
>>>
>>> void somefunc(void) {
>>> unsigned char c;
>>> /* ... */
>>> }
>>>
>>> 'c' is permitted to have a trap representation due to its
>>> "provenance,"

>>
>> It isn't. You are either mis-remembering or have misunderstood.

>
> Committee Discussion in Defect Report #260: [snip]


The type unsigned char does not have trap representations. There
are no exceptions. Types that don't have trap representations
never have a trap representation.

In C11, accessing a variable like 'c' above before it has been
initialiized is undefined behavior. But that is because C11
added (relative to, eg, N1256) a specific statement regarding
such cases, stating explicitly that the behavior is undefined;
it has nothing to do with provenance or trap representations.
Indeed, seeing that this proviso was added in C11 makes it
obvious that DR 260 doesn't apply to cases like the example
above, because otherwise there would be no reason to add it.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
'Class.inherited' v. 'inherited' syntax inside Class 7stud -- Ruby 11 11-09-2007 06:45 PM
Packed structs vs. unpacked structs: what's the difference? Daniel Rudy C Programming 15 04-10-2006 08:10 AM
Array of structs instead of an array with pointers to structs? Paminu C Programming 5 10-11-2005 07:18 PM
const structs in other structs Chris Hauxwell C Programming 6 04-27-2004 07:03 PM
structs with fields that are structs Patricia Van Hise C Programming 5 04-05-2004 01:37 AM



Advertisments