Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
OUTPUT
The size of int: 2-byte
The size of a_union: 8-byte
The size of a_struct: 10-byte
The purpose of the program in Listing 20.3 is to show the difference between a
ANALYSIS
union memory allocation and a structure memory allocation, although both the union and the structure consist of the same members.
A union, called a_union, is defined in lines 7–10; it has two members, a double variable x and an int variable y. In addition, a structure, called a_structure and defined in lines 12–15, also consists of two members, a double variable x and an int variable y.
26 067231861x CH20 1/25/00 10:59 AM Page 341
Understanding Unions
341
The statements in lines 17–20 first measure the sizes of the double and int data types on the host machine. For instance, on my machine, the size of the double data type is 8
bytes long and the int data type is 2 bytes long.
Then lines 22–25 measure the sizes of the a_union union and the a_structure structure, respectively. From the output, you see that the size of a_union is 8 bytes long on my machine.
The size of the structure, on the other hand, is 10 bytes on my machine, because there has to be enough memory space for both the double and the int in the structure.
Using Unions
Now let’s focus on the applications of unions. Basically, there are two kinds of union applications, which are introduced in the following two sections.
Referencing the Same Memory Location Differently
The first application of unions is to reference the same memory location with different union members.
To get a better idea about referencing the same memory with different union members, let’s have a look at the program in Listing 20.4, which uses the two members of a union to reference the same memory location. (You assume that the char data type is 1 byte long, and the int data type is 2 bytes long.)
LISTING 20.4
Referencing the Same Memory Location with Different
TYPE
Union Members
1: /* 20L04.c: Referencing the same memory in different ways */
2: #include
3:
4: union u{
5: char ch[2];
20
6: int num;
7: };
8:
9: int UnionInitialize(union u val);
10:
11: main(void)
12: {
13: union u val;
14: int x;
15:
continues
26 067231861x CH20 1/25/00 11:00 AM Page 342
342
Hour 20
LISTING 20.4
continued
16: x = UnionInitialize(val);
17:
18: printf(“The two characters held by the union:\n”);
19: printf(“%c\n”, x & 0x00FF);
20: printf(“%c\n”, x >> 8);
21:
22: return 0;
23: }
24: /* function definition */
25: int UnionInitialize(union u val)
26: {
27: val.ch[0] = ‘H’;
28: val.ch[1] = ‘i’;
29:
30: return val.num;
31: }
The following output is displayed on the screen of my computer after the executable 20L04.exe is created and executed:
The two characters held by the union:
OUTPUT
H
i
As you see from the program in Listing 20.4, a union called val is defined in
ANALYSIS
line 13, which contains two members. One is a char array ch and the other is an int variable num. If a char data type is 1 byte long and an int data type is 2 bytes long, the ch array and the integer variable num have the same length of memory storage on those machines.
A function named UnionInitialize()is called and passed the union name val in line 16. The definition of the UnionInitialize() function is shown in lines 25–31.
From the function definition, you can see that the two elements of the char array ch are initialized with two character constants, ‘H’ and ‘i’ (in lines 27 and 28). Because the char array ch and the int variable num share the same memory location, you can return the value of num that contains the same content as the ch array. (See line 30.) Here you’ve used the two members, ch and num, in the val union to reference the same memory location and the same contents of the union.
The value returned by the UnionInitialize() function is assigned to an int variable x in line 16 inside the main() function. The statements in lines 19 and 20 print out the two bytes of the int variable num. Each byte of num corresponds to a character that was used to initialize the ch array because num and ch are both in the same union and have the same content as the union. Line 19 displays the low byte of num, obtained by evaluating 26 067231861x CH20 1/25/00 11:00 AM Page 343
Understanding Unions
343
the x & 0x00FF expression. In line 20, the high byte of num is obtained by shifting the x variable to the right by 8 bits, that is, by using the shift-right operator in the x >> 8
expression. (The bitwise operator (&) and the shift operator (>>) were introduced in Hour 8, “Using Conditional Operators.”)
From the output, you can see that the content of the val union is shown on the screen correctly.
Figure 20.2 shows the locations of the two character constants in memory.
FIGURE 20.2
Low
The memory locations
0
x
1000
of the two character
'H'
constants.
0
x
1001
' i '
0
x
1002
High
(Assume that 0
x
1000 is the start address.)
There are two formats to store a multiple-byte quantity, such as the int variable num in Listing 20.4. One of the formats is called the
little-endian
format; the other is the
big-endian
format.
For the little-endian format, the high bytes of a multiple-byte quantity are stored at higher memory addresses and the low bytes are saved at lower
addresses. The little-endian format is used by Intel’s 80x86 microprocessors.
My computer’s CPU is a Pentium microprocessor, which is one of the mem-
bers in the 80x86 family. Therefore, in Listing 20.4, the character constant
‘H’, which is a low byte, is stored at the lower address. ‘i’ is stored at the higher address because it’s a high byte.
The big-endian format is just the opposite. That is, the high bytes are stored at lower addresses; the low bytes are stored at higher addresses. Motorola’s
20
68000 microprocessor family uses the big-endian format.
Making Structures Flexible
The second application of unions is to nest a union inside a structure so that the structure can hold different types of values.
For example, suppose you want to write a program that asks the user about the name of a cable company or a satellite dish company that provides service to the user. Assume that the user either uses a cable or a satellite dish at home, but not both. Then, if you define 26 067231861x CH20 1/25/00 11:00 AM Page 344
344
Hour 20
two character arrays to store the cable company and satellite dish company names respectively, one of the arrays will be empty due to the assumption. In this case, you can declare a union with the two character arrays as its members so that the union can hold either a cable company name or a satellite dish company name, depending on the user’s input. Listing 20.5 demonstrates how to write a program with such a union.
TYPE
LISTING 20.5
Making a Structure Flexible
1: /* 20L05.c: Using unions */
2: #include
3: #include
4:
5: struct survey {
6: char name[20];
7: char c_d_p;
8: int age;
9: int hour_per_week;
10: union {
11: char cable_company[16];
12: char dish_company[16];
13: } provider;
14: };
15:
16: void DataEnter(struct survey *s);
17: void DataDisplay(struct survey *s);
18:
19: main(void)
20: {
21: struct survey tv;
22:
23: DataEnter(&tv);
24: DataDisplay(&tv);
25:
26: return 0;
27: }
28: /* function definition */
29: void DataEnter(struct survey *ptr)
30: {
31: char is_yes[4];
32:
33: printf(“Are you using cable at home? (Yes or No)\n”);
34: gets(is_yes);
35: if ((is_yes[0] == ‘Y’) ||
36: (is_yes[0] == ‘y’)){
37: printf(“Enter the cable company name:\n”);
38: gets(ptr->provider.cable_company);
39: ptr->c_d_p = ‘c’;
40: } else {
41: printf(“Are you using a satellite dish? (Yes or No)\n”);
26 067231861x CH20 1/25/00 11:00 AM Page 345
Understanding Unions
345
42: gets(is_yes);
43: if ((is_yes[0] == ‘Y’) ||
44: (is_yes[0] == ‘y’)){
45: printf(“Enter the satellite dish company name:\n”);
46: gets(ptr->provider.dish_company);
47: ptr->c_d_p = ‘d’;
48: } else {
49: ptr->c_d_p = ‘p’;
50: }
51: }
52: printf(“Please enter your name:\n”);
53: gets(ptr->name);
54: printf(“Your age:\n”);
55: scanf(“%d”, &ptr->age);
56: printf(“How many hours you spend on watching TV per week:\n”);
57: scanf(“%d”, &ptr->hour_per_week);
58: }
59: /* function definition */
60: void DataDisplay(struct survey *ptr)
61: {
62: printf(“\nHere’s what you’ve entered:\n”);
63: printf(“Name: %s\n”, ptr->name);
64: printf(“Age: %d\n”, ptr->age);
65: printf(“Hour per week: %d\n”, ptr->hour_per_week);
66: if (ptr->c_d_p == ‘c’)
67: printf(“Your cable company is: %s\n”,
68: ptr->provider.cable_company);
69: else if (ptr->c_d_p == ‘d’)
70: printf(“Your satellite dish company is: %s\n”,
71: ptr->provider.dish_company);
72: else
73: printf(“You don’t have cable or a satellite dish.\n”);
74: printf(“\nThanks and Bye!\n”);
75: }
When the executable program 20L05.exe is being run, I enter my answers to the survey and the following output is displayed (my answers are shown in bold type in the output):
20
Are you using cable at home? (Yes or No)
OUTPUT
No
Are you using a satellite dish? (Yes or No)
Yes
Enter the satellite dish company name:
ABCD company
Please enter your name:
Tony Zhang
Your age:
30
How many hours you spend on watching TV per week:
8
26 067231861x CH20 1/25/00 11:00 AM Page 346
346
Hour 20
Here’s what you’ve entered:
Name: Tony Zhang
Age: 30
Hour per week: 8
Your satellite dish company is: ABCD company
Thanks and Bye!
As you can see in lines 5–14, a structure data type with the tag name survey
ANALYSIS
is declared, and in it a nested union called provider has two members, the cable_company array and the dish_company array. The two members of the union are used to hold the names of cable or satellite dish companies, depending on the user’s input.
The statements in lines 16 and 17 declare two functions, DataEnter() and DataDisplay(), in which a pointer with struct survey is passed to each function as its argument.
A structure called tv is defined in line 21 inside the main() function. Then in lines 23
and 24, the DataEnter() and DataDisplay() functions are each called with the address of the tv structure as the argument.
Lines 29–58 contain the definition of the DataEnter() function, which asks the user to enter proper information based on the survey questions. Under the assumption you made earlier, the user can use either cable or a satellite dish, but not both. If the user does use cable, line 38 receives the cable company name entered by the user and saves it into the memory storage referenced by one of the members in the provider union, cable_company.
If the user uses a satellite dish, line 46 stores the satellite dish company name entered by the user into the same location of the provider union. But this time the name of another union member, dish_company, is used to reference the memory location. Now you see how to save the memory by putting two exclusive data items into a union.
In fact, the program supports another situation in which the user neither has cable nor a satellite dish. In this case, the char variable c_d_p, which is a member of the structure, is assigned with the character constant ‘p’.
Lines 60–75 give the definition of the DataDisplay() function that prints out the information entered by the user back to the screen. The output shown here is a sample I made by running the executable program of Listing 20.5 on my machine.
26 067231861x CH20 1/25/00 11:00 AM Page 347
Understanding Unions
347
Defining Bit Fields with
struct
In this section, you’ll revisit our old friend the struct keyword to declare a very small object. Then you’ll use the object with unions.
As you know, char is the smallest data type in C. The char data type is one byte long.
However, with the help of the struct keyword, you can declare a smaller object—a
bit
field
—which allows you to access a single bit. A bit is able to hold only one of two values, 1 or 0.
The general form to declare and define bit fields is
struct tag_name {
data_type name1: length1;
data_type name2: lenght2;