Is it legal and well-defined to use a union to transform between two structures with a common initial sequence (see Example)?

I have an API with an open structure A and an internal structure B and should be able to convert structure B to structure A. Is the following code legal and well-defined behavior in C99 (and VS 2010 / C89) and C ++ 03 / C ++ eleven? If so, explain what makes it clear. If this is not the case, then what is the most efficient and cross-platform tool for converting between two structures?

struct A { uint32_t x; uint32_t y; uint32_t z; }; struct B { uint32_t x; uint32_t y; uint32_t z; uint64_t c; }; union U { struct A a; struct B b; }; int main(int argc, char* argv[]) { U u; ubx = 1; uby = 2; ubz = 3; ubc = 64; /* Is it legal and well defined behavior when accessing the non-write member of a union in this case? */ DoSomething(uax, uay, uaz); return 0; } 


UPDATE

I simplified the example and wrote two different applications. One is based on memcpy, and the other is a union.


Union:

 struct A { int x; int y; int z; }; struct B { int x; int y; int z; long c; }; union U { struct A a; struct B b; }; int main(int argc, char* argv[]) { U u; ubx = 1; uby = 2; ubz = 3; ubc = 64; const A* a = &u.a; return 0; } 


Tetr:

 #include <string.h> struct A { int x; int y; int z; }; struct B { int x; int y; int z; long c; }; int main(int argc, char* argv[]) { B b; bx = 1; by = 2; bz = 3; bc = 64; A a; memcpy(&a, &b, sizeof(a)); return 0; } 



Profiled assembly [DEBUG] (Xcode 6.4, default C ++ compiler):

Here is the corresponding build difference for debug mode. When I profiled release builds, there were no differences in the build.


Union:

 movq %rcx, -48(%rbp) 


Tetr:

 movq -40(%rbp), %rsi movq %rsi, -56(%rbp) movl -32(%rbp), %edi movl %edi, -48(%rbp) 



Reservation:

The merge-based sample code triggers a warning that the variable 'a' is not used. Since the profiled assembly is being debugged, I don't know if there are any consequences.

+15
c ++ c c99 c89 unions
Jul 22 '15 at 3:36
source share
2 answers

This is great because the members you are referring to are elements of a common source sequence.

C11 ( 6.5.2.3 Elements of structure and association ; Semantics ):

[...] if the union contains several structures that have a common initial sequence (see below), and if the union object currently contains one of these structures, it is allowed to check the common initial part of any of them in any place where it is visible declaration of the completed type of association. Two structures have a common initial sequence if the corresponding members have compatible types (and for bit fields, the same width) for a sequence of one or more initial elements.

C ++ 03 ( [class .mem] / 16 ):

If the POD-union contains two or more POD structures that have a common initial sequence, and if the POD-union object currently contains one of these POD structures, it is allowed to check the common initial part of any of them. Two POD structures have a common initial sequence if the corresponding members have types compatible with layouts (and for bit fields, the same width) for a sequence of one or more initial elements.

Other versions of the two standards have a similar language; since C ++ 11 uses terminology, this is a standard layout, not a POD.




I think confusion may have arisen because C allows type-punning (overlapping a member of another type) through union, where C ++ does not; this is the main case when you must use memcpy to ensure compatibility with C / C ++. But in your case, the elements you refer to are of the same type and are preceded by members of compatible types, so the rule for choosing a type does not matter.

+11
Jul 22 '15 at 8:29
source share

This is legal in both C and C ++.

For example, in C99 (6.5.2.3/5) and C11 (6.5.2.3/6):

To simplify the use of unions, there is one special guarantee: if the connection contains several structures that have a common initial sequence (see below), and if the union object currently contains one of these structures, it is allowed to check the common initial part of any of them anywhere where the declaration of the full union type is visible. Two structures have a common initial sequence if the corresponding members have compatible types (and for bit fields, the same width) for a sequence of one or more initial elements.

Similar provisions exist in C ++ 11 and C ++ 14 (different wording, same meaning).

+5
Jul 22 '15 at 8:31
source share



All Articles