Eine union ist etwas sehr Sonderbares. Denn in einer union teilen sich alle Member den selben Speicher.
Man könnte einen dynamischen Typen (der zB sowohl int als auch double sein kann) naiverweise so implementieren:
#include <iostream>
using namespace std;
struct IntAndDouble
{
bool isInt;
union
{
int Int;
double Double;
};
};
void setInt(IntAndDouble& s, int value)
{
s.Int = value;
s.isInt = true;
}
void setDouble(IntAndDouble& s, double value)
{
s.Double = value;
s.isInt = false;
}
void printValue(IntAndDouble const& s)
{
if(s.isInt)
{
cout<<s.Int;
}
else
{
cout<<s.Double;
}
}
int main()
{
IntAndDouble s;
setInt(s, 7);
printValue(s);
}
Int und Double teilen sich den selben Speicher. Wenn wir s.Int=7; cout<<s.Double; schreiben würden, würden wir Blödsinn erhalten. Denn wir haben dem Speichern einen int zugewiesen und versuchen diesen int nun als double zu interpretieren, was natürlich nicht gut gehen kann.
Wir wissen, dass struct Werte zusammenfassen kann. Da diese Werte hintereinander im Speicher liegen müssen, lässt sich mit struct und union auch folgendes basteln:
#include <iostream>
using namespace std;
union Test
{
int Int; //Annahme: sizeof(int) == 4
struct
{
char byte1;
char byte2;
char byte3;
char byte4;
} Bytes;
//char bytes[4]; //so könnte man es auch machen (statt der struct)
//man würde dann statt t.Bytes.byte1 einfach t.bytes[0] schreiben - das ist geschmackssache.
};
int main()
{
Test t;
t.Int = 7;
cout<<"Der int-Wert "<<t.Int<<" besteht aus folgenden Bytes:\n";
cout<<"Byte 1: "<<t.Bytes.byte1<<'\n';
cout<<"Byte 2: "<<t.Bytes.byte2<<'\n';
cout<<"Byte 3: "<<t.Bytes.byte3<<'\n';
cout<<"Byte 4: "<<t.Bytes.byte4<<'\n';
}
Wobei man bei sowas natürlich aufpassen muss, dass sizeof(int) auch wirklich sizeof(char)*4 entspricht. Auf gängigen 32Bit Plattformen ist dies so.