Unions

A union is a construct in C that can hold one of several types. A union variable can only hold one value at a time, unlike a struct, but that value is not restricted to a single type.

Here’s the format for declaring a union:

union modelName {
    type1 name1;
    type2 name2;
    ...
    typeN nameN;
} objectList;

Here, modelName is the name of the type you’re creating, each type name is a type you want to be able to store in this union plus a name for it, and objectList is a list of variables you want to create with this union type. Both modelName and objectList are optional.

Defining Unions

Suppose I want a type that will allow me to store money as dollars (in a double field) or as yen (in an int field). Here’s how I’d do that:

union money {
    double dollars;
    int yen;
} price;

This creates a variable called price that has type union money.

I could also create a variable called price2 like this:

union money price2;

Union Fields

To access a field in a union, use the . notation – just like with a struct:

variableName.fieldName

(variableName is the name of the union variable, and fieldName is the name of the field that we want to access.) If we had a pointer to a union variable, we would use the -> notation to access a field.

For example, I can make the price variable hold 17 yen:

price.yen = 17;

Or I can make the price variable hold $4.20:

price.dollars = 4.20;

Note that when I change the value of price from 17 yen to $4.20, I lose the information about 17 yen. A union variable can only hold one value at a time, but that value can have different types.

Unions and Enums

Suppose we have a variable of type union money, and we want to print out its value (in either dollars or yen, depending on what is stored). With what we’ve done so far, there is no way to tell WHICH type is stored in a union variable – it might be yen and it might be dollars. If we do:

union money cost;
cost.yen = 17;
printf("Dollars: %lf\n", cost.dollars);

which sets the yen field but then prints the dollars field, the behavior is undefined. This code is likely to crash or at least have unpredictable results.

To solve this problem, we need to keep track of which type we’re storing in a union variable. The most common way to do this is with an enum. For example, we could declare:

enum costK {dollarsK, yenK};

Then if we did:

union money cost;
cost.yen = 17;

We could also do:

enum costK costEnum;
costEnum = yenK;

Then we could tell which type was stored in our union variable by checking the value of our enum variable:

if (costEnum == yenK) }
    printf("Yen: %d\n", cost.yen);
}
else if (costEnum == dollarsK) {
    printf("Dollars: %lf\n", cost.dollars);
}

We will print the value of the field that we’re currently using in the union.

Structs inside Unions

A struct is a valid type, so we can have a struct be a field in a union. For example, suppose we wanted to create an animal union that could be either a dog (struct type) or a cat (struct type). Furthermore, we want to keep track of the name and breed of the dog, and the name and color of the cat.

Here’s how:

union animal {
    struct {
        char name[20];
        char breed[20];
    } dog;
    struct {
        char name[20];
        char color[20];
    } cat;
};

Notice that we don’t name the dog struct or the cat struct because we only want a single variable of that type. Instead, we create a single dog variable and a single cat variable. Here’s how we create a union variable that holds an orange cat called Tiger:

union animal kitty;
strcpy(kitty.cat.name, "Tiger");
strcpy(kitty.cat.color, "orange");

Unions inside Structs

Similarly, we can put a union inside a struct. For example, suppose we wanted to keep track of someone’s name, what country they’re from, and how much their bill is (in the appropriate currency). We could store the country with an enum, and the bill (with different currency) as a union. Here’s how:

struct guest {
    char name[20];
    enum {USA, France, Japan} country;
    union {
        double dollars;
        double euro;
        int yen;
    } cost;
};

Now if we want the guest Maria from France, who paid 100 euro, here’s what we’d do:

struct guest maria;
strcpy(maria.name, "Maria");
maria.country = France;
maria.cost.euro = 100.0;

Unions as Inheritance

The union construct allows us to simulate inheritance from other languages. For example, suppose we defined the following abstract class in C#:

public abstract class Person {
    public string name;
    public int age;
}

Suppose as well that we have two C# classes, Student and Employee, which extend Person:

public class Student : Person {
    public string major;
    public double gpa;
}

public class Employee extends Person {
    public string division;
    public int yearsWorked;
}

The equivalent in C would be a person structure that holds a name, age, and a union field. The union field could hold either a student struct (which has a major and gpa) or an employee struct (which has a division and a yearsWorked). We would also need an enum field that keeps track of which type in the union is active.

Here’s what it would look like:

struct person {
    char name[20];
    int age;
    union {
        struct {
            char major[20];
            double gpa;
        } student;
        struct {
            char division[20];
            int yearsWorked;
        } employee;
    } type;
    enum {employeeK, studentK} typeK;
};

Suppose a student has the following information:

  • Name: Bob Jones
  • Age: 18
  • Major: CMPEN
  • GPA: 3.2

Here’s how we would create a variable to represent that student:

struct person bob;
strcpy(bob.name, "Bob Jones");
bob.age = 18;
strcpy(bob.type.student.major, "CMPEN");
bob.type.student.gpa = 3.2;
bob.typeK = studentK;