Previous: 12.3 Sorting Arrays of Structures
Up: 12 Structures and Unions
Next: 12.5 Common Errors
Previous Page: 12.3 Sorting Arrays of Structures
Next Page: 12.5 Common Errors
In some applications, we might want to maintain information of one of two alternate forms. For example, suppose, we wish to store information about a person, and the person may be identified either by name or by an identification number, but never both at the same time. We could define a structure which has both an integer field and a string field; however, it seems wasteful to allocate memory for both fields. (This is particularly important if we are maintaining a very large list of persons, such as payroll information for a large company). In addition, we wish to use the same member name to access identity the information for a person.
C provides a data structure which fits our needs in this case called a union data type. A union type variable can store objects of different types at different times; however, at any given moment it stores an object of only one of the specified types. The declaration of a union type must specify all the possible different types that may be stored in the variable. The form of such a declaration is similar to declaring a structure template. For example, we can declare a union variable, person, with two members, a string and an integer. If the name is entered, we will use person to store the string; if an identification number is entered, we will use person to store an integer. Here is the union declaration:
union {
int id;
char name[25];
} person;
This declaration differs from a structure in that, when memory is allocated
for the variable person, only enough memory is allocated
to accommodate the largest of the specified types.
The memory allocated for person will be large enough to store
the larger of an integer or an 25 character array.
Like structures,
we can define a tag for the union, so the union template
may be later referenced by name:
union human {
int id;
char name[25];
} person;
Likewise,
it is possible to declare just a tag, and later, use the tag to declare
variables:
union human {
int id;
char name[25];
};
union human person, *ppers;
The syntax for declaring a union type is basically the same as for a structure:
ppers = &person;
person.id = 12;
if (ppers->id == 12)
...
printf("Id = %d\n", person.id);
The type of data accessed is determined by the member name used to
qualify the variable name.
In our example, person.id will access an integer; while
person.name will access a string (a character pointer).
Since at any given time, the contents of the union variable may be one of several types ( int or string for person), we must keep track what type of data is stored in order to access the information correctly. Each time an object is stored in a union variable, it is the programmer's responsibility to keep track of the type stored. If an attempt is made to retrieve a type different from the type last stored, the result is sure to be strange and incorrect. The specific behavior is implementation dependent.
To remember the type of object last stored in a union variable, it is common to store that information in a variable. The best way is to declare a structure containing both the union variable as a field and another field that indicates the type of data stored in the union. For example, we can declare such a structure type and a structure array as follows:
#define NAME 0
#define ID 1
struct record {
int ptype;
union human person;
};
struct record list[MAX];
Now, as we read information about each element of list, if the
information is numeric, we store it as id;
otherwise, we store it as name. We
also store the type, ID or NAME in the member, ptype.
Figure 12.15 shows a function that reads
identifying information about each person and
stores it in the union type member. Depending on the type of
information read, it uses the appropriate union field name,
and stores the type in the
ptype field of the structure.
The loop body in the function looks at the first character of the input string, s. If it is a digit, then the data is an id number so INT is stored in ptype, and the string is converted to an integer (using atoi()) and stored in the union id field. If the first character of s is not a digit, NAME is stored in ptype, and the string is copied into the union name field.
It is now easy to write a function that prints the identifying information
stored in the list. Since each record includes the type of information stored in
the union variable, it is easy to retrieve the information correctly
as shown in Figure 12.16.
We now write a simple program that first reads a list of identifying
information about a group of people, and later prints the list. The
identifying information may be either a name or an id number.
The structure and
union declarations as well as constant definitions are included in the file
unidef.h shown together with the code in Figure 12.16.
Sample Session:
The above program can be written in many alternate ways. We have written the program to illustrate the use of union variables.