#define bool unsigned char #define true 1 typedef enum {INT, BOOL, STRING} ValTag; typedef struct { ValTag tag; union { int intVal; bool boolVal; char* stringVal; } val; } IntOrBoolOrString; IntOrBoolOrString ibs; int i; bool b; char* s; int main(int argc, char** argv) { /* * Use ibs as an int. */ ibs.val.intVal = 10; // Assign the intVal field ibs.tag = INT; // Keep track that ibs is currently an int i = ibs.val.intVal; // Assign 10 to i, as expected /* * Erroneously use ibs as a string, with int as the current component. */ s = ibs.val.stringVal; // Assigns effective garbage to s; it's the // programmer's responsibility to keep track of // which union field is currently active /* * Use ibs as a boolean, again with programmer-maintained change of tag. */ ibs.val.boolVal = true; ibs.tag = BOOL; b = ibs.val.boolVal; /* * Use ibs as a string, again with programmer-maintained change of tag. */ ibs.val.stringVal = "xyz"; ibs.tag = STRING; s = ibs.val.stringVal; /* * Here is the normal way to safely use a union variable. It involves * checking the current value of the tag, and accessing the field that the * tag-check says is currently active. This is the place in C where the * programmer-maintained tag value used. There are a number of ways to * implement it. The use of an enum like ValTag is pretty typical. */ if (ibs.tag == INT) { i = ibs.val.intVal; // Safely fetch the int value // do whatever with i } if (ibs.tag == BOOL) { b = ibs.val.boolVal; // Safely fetch the bool value // do whatever with b } if (ibs.tag == STRING) { s = ibs.val.stringVal; // Safely fetch the string value // do whatever with s } /* * Try to assign an integer to the whole union variable. */ // ibs = 10; // Produces a compiler type error. }