Terminology wise, my understanding is that a data structure is a container -- an array, vector, list, tree, graph, stack, etc type entity.
struct and class and union are 'complex types' or 'aggregate types' like std::string.
union is sort of a special snowflake, to be used with extreme caution. Union members share the same memory block!!
union u
{
int x;
char c[4];
};
u test;
test.x = 1234;
//the above statement ALSO ASSIGNED test.c TO THE RAW BYTES OF THE INTEGER 1234.
likewise if you change test.c, test.x will be modified. Its identical to this:
int x;
int * ip = &x;
char *cp = (char*)(&x);
if you change the target of either pointer, you change the target of the other pointer.
standard c++ says that you cannot access more than 1 member of any union variable in your code, otherwise behavior is undefined. However most (all?) compilers just let you do it and when you do, it just re-defines how the raw bytes at the memory address are understood. You see a lot of that in older code.
that is technically the undefined behavior I mentioned. Every compiler I know of, its well defined to do as you said, but the standard document disagrees.
you cannot access more than ONE of the fields of a union for any given variable of that type.
eg
a.i = 10; //OK
a.d[i]= 1; //NOT ok, used a second field of the union.
it WORKS, but the standard has it as undefined behavior. I grew up thinking this was ok, so it was a recent thing for me to learn as well.
honestly this feels like a poor move by the standard committee. Now the only use union serves is some sort of space saving polymorphic entity that can be handled better ways. The bytewise access can still be done with pointer casting, so you could make a union class that is standard compliant with a bit of pointer magic and casting inside.
that describes half of C++'s low level features. memory functions (memcpy etc), pointers, arrays, unions, etc can all blow up if you are careless, and are all powerful if used correctly. Even a vector will blow up if you run out of bounds on it.
you cannot access more than ONE of the fields of a union for any given variable of that type.
Unions have an idea of an active variant member. This is the field of a union (i.e., the "variant member") that's been written to last.
It's possible to change the active variant member by writing to another. The only restriction is that you can't read from a variant member that is not active.
This example is well defined:
1 2
a.i = 24; // a.i is the active variant member
a.d[1] // a.d is the active variant member