Letter to Unions, with gcc

Consider the following types:

struct A { int x; }; struct B { int y; char z; }; union U { A a; B b; }; 

And this piece of code:

 U u; new (&u.b) B; by = 42; bz = 'x'; 

At this point, reading from uax is the correct behavior (and will give 42 ), because it is in a common source sequence. The uax is undefined behavior.

But gcc allows you to enter a pool through a union - documents explicitly allow reading of an inactive element, regardless of the sequence of elements. Does gcc provide the ability to write to an inactive element, even a common starting sequence, or will it still be undefined for gcc? That is, if at the moment I had:

 void write(A& arg) { ax = 17; } write(&u.a); // generally undefined behavior, since active member is ub // but does gcc allow it? f(uby); // is this definitely f(17)? g(ubz); // ... and is this definitely g('x')? 
+7
c ++ gcc unions g ++
source share
1 answer

The behavior you describe (called "type-punning") is allowed, but undefined by the C ++ specification (see note below). It can be defined under a specific compiler and equipment. More specifically, on gcc and x86 with simple types (char, short, float, double, ...) this will act as an interpretation between the various fields.

... The practice of reading from a different union member than the one that was recently written (called type casting) is commonplace. Even with -fstrict-aliasing use of a pool is allowed, provided that the memory is accessed through a union type. ( source )

In addition, it is sometimes useful, for example, when reading from a device (such as a socket):

 union { struct { int a; char b; short c; } data; char buf[128]; } u; read_from_device(u.buf, 128); printf("Data (a,b,c): (%d,%d,%d)\n", u.data.a, u.data.b, u.data.c); 

First we read the raw data from the device, then we use the structure to rethink it as numbers. We often use the #pragma pack on a struct to ensure that the data is packaged the same way that it is packaged on the device.

Active Item Note

The definition of the active member is implicit and determined solely by the programmer, not the compiler. You decide which member is active. The field's lifespan begins when you assign it.

... the beginning of his life is sequenced after calculating the value of the left and right operands and before the appointment.

The fact that management indicates that writing to an inactive field makes it active suggests that it is allowed.

For more information see this :

Item lifetime

The member's lifetime begins when the member is activated. If another participant was previously active, his life span ends.

When an active member of a union is switched by an assignment expression of the form E1 = E2, which uses either the built-in assignment operator or the trivial assignment operator, for each member of the union X that appears in access subexpressions of the member and substring of the array E1, which is not a class with non-trivial or remote constructors by default, if the modification X will have undefined behavior with an alias of the rule type, an object of type X is implicitly created in the nominated storage; initialization is not performed, and the beginning of its lifetime is sequenced after calculating the values โ€‹โ€‹of the left and right operands and before the assignment.

 union A { int x; int y[4]; }; struct B { A a; }; union C { B b; int k; }; int f() { C c; // does not start lifetime of any union member cbay[3] = 4; // OK: "cbay[3]", names union members cb and cbay; // This creates objects to hold union members cb and cbay return cbay[3]; // OK: cbay refers to newly created object } struct X { const int a; int b; }; union Y { X x; int k; }; void g() { Y y = { { 1, 2 } }; // OK, yx is active union member (9.2) int n = yxa; yk = 4; // OK: ends lifetime of yx, yk is active member of union yxb = n; // undefined behavior: yxb modified outside its lifetime, // "yxb" names yx, but X default constructor is deleted, // so union member yx lifetime does not implicitly start } 
+1
source share

All Articles