Submitted by tushar pramanick on Sun, 03/10/2013 - 21:56

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 we 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 we define 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, we 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 <stdio.h>
3:  #include <string.h>
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");
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 have the following output displayed on the screen (my answers are shown in bold monospace type in the output):

OUTPUT

C:\app>20L05
Are you using cable at home? (Yes or No)
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

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!
C:\app>

ANALYSIS

As you can see in lines 5_14, a structure data type with the tag name survey 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 the two functions as the 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 invoked with the address of the tv structure as their 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 we made earlier, the user can use either cable or a satellite dish, but not both. If the user does use cable, then 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, then 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 has neither 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.
Defining Bit Fields with struct

In this section, we'll revisit our old friend the struct keyword to declare a very small object. Then we'll use the object with unions.

As you know, char is the smallest data type in C. On many machines, the char data type is 8 bits long. However, with the help of the struct keyword, we can declare a smaller object—a bit field—which allows you to access a single bit. A bit is able to hold one of the 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;
   . . .
   data_type nameN: lengthN;
} variable_list;



Here the struct keyword is used to start the declaration. tag_name is the tag name of the struct data type. data_type, which must be either int, unsigned, or signed, specifies the data type of bit fields. names1, name2, and nameN are names of bit fields. length1, length2, and lengthN indicate the lengths of bit fields, which may not exceed the length of the int data type. variable_list contains the variable names of the bit field.

For instance, the following statement defines a variable called jumpers with three bit fields:

struct bf {
   int jumper1: 1;
   int jumper2: 2;
   int jumper3: 3;
} jumpers;


Here jumper1, jumper2, and jumper3 are the three bit fields with lengths of 1 bit, 2 bits, and 3 bits, respectively. Figure 20.3 demonstrates the memory allocations of the three bit fields.


Figure 20.3. The memory allocations of jumper1, jumper2, and jumper3.

The program in Listing 20.6 is an example of using the bit fields defined with struct. In fact, the program in Listing 20.6 is a modified version of the program in Listing 20.5.

TYPE
Listing 20.6. Applying bit fields.


1:  /* 20L06.c: Applying bit fields */
2:  #include <stdio.h>
3:  #include <string.h>
4:
5:  struct bit_field {
6:     int cable: 1;
7:     int dish: 1;
8:  };
9:
10: struct survey {
11:    char name[20];
12:    struct bit_field c_d;
13:    int age;
14:    int hour_per_week;
15:    union {
16:        char cable_company[16];
17:        char dish_company[16];
18:    } provider;
19: };
20:
21: void DataEnter(struct survey *s);
22: void DataDisplay(struct survey *s);
23:
24: main(void)
25: {
26:    struct survey tv;
27:
28:    DataEnter(&tv);
29:    DataDisplay(&tv);
30:
31:    return 0;
32: }
33: /* function definition */
34: void DataEnter(struct survey *ptr)
35: {
36:    char is_yes[4];
37:
38:    printf("Are you using cable at home? (Yes or No)\n");
39:       gets(is_yes);
40:    if ((is_yes[0] == `Y') ||
41:        (is_yes[0] == `y')){
42:       printf("Enter the cable company name:\n");
43:       gets(ptr->provider.cable_company);
44:       ptr->c_d.cable = 1;
45:       ptr->c_d.dish = 0;
46:    } else {
47:       printf("Are you using a satellite dish? (Yes or No)\n");
48:          gets(is_yes);
49:       if ((is_yes[0] == `Y') ||
50:           (is_yes[0] == `y')){
51:          printf("Enter the satellite dish company name:\n");
52:          gets(ptr->provider.dish_company);
53:          ptr->c_d.cable = 0;
54:          ptr->c_d.dish = 1;
55:       } else {
56:          ptr->c_d.cable = 0;
57:          ptr->c_d.dish = 0;
58:       }
59:    }
60:    printf("Please enter your name:\n");
61:       gets(ptr->name);
62:    printf("Your age:\n");
63:       scanf("%d", &ptr->age);
64:    printf("How many hours you spend on watching TV per week:\n");
65:       scanf("%d", &ptr->hour_per_week);
66: }
67: /* function definition */
68: void DataDisplay(struct survey *ptr)
69: {
70:    printf("\nHere's what you've entered:\n");
71:    printf("Name: %s\n", ptr->name);
72:    printf("Age:  %d\n", ptr->age);
73:    printf("Hour per week: %d\n", ptr->hour_per_week);
74:    if (ptr->c_d.cable && !ptr->c_d.dish)
75:       printf("Your cable company is: %s\n",
76:          ptr->provider.cable_company);
77:    else if (!ptr->c_d.cable && ptr->c_d.dish)
78:       printf("Your satellite dish company is: %s\n",
79:          ptr->provider.dish_company);
80:    else
81:       printf("You don't have cable or a satellite dish.\n");
82:    printf("\nThanks and Bye!\n");
83: }


Because the program in Listing 20.6 is basically the same as the one in Listing 20.5, I have the same output shown on the screen after I run the executable 20L06.exe and enter the same answers to the survey:

OUTPUT

C:\app>20L06
Are you using cable at home? (Yes or No)
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

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!
C:\app>

ANALYSIS

The purpose of the program in Listing 20.6 is to show you how to declare bit fields and how to use them. As you can see in lines 5_8, two bit fields, cable and dish, are declared with the struct data type. Each of the bit fields is 1 bit long. Then a structure called c_d is defined with the two bit fields in line 12, which is within another structure declaration from line 10 to line 19.

The bit fields cable and dish are used as flags to indicate whether the user is using cable or a satellite dish based on the answers made by the user. If the user has cable, then the cable bit field is set to 1 and the dish bit field is set to 0. (See lines 44 and 45.) On the other hand, if the user has a satellite dish, then dish is set to 1 and cable is set to 0, as shown in lines 53 and 54. If, however, the user has neither cable nor a satellite dish, both cable and dish are set to 0 in lines 56 and 57.

So you see, we've used the combinations of the two bit fields, cable and dish, to represent the three situations: having cable, having a satellite dish, or having neither cable nor a satellite dish.

Since the program in Listing 20.6 is basically the same as the one in Listing 20.5, I get the same output after I run the executable program of Listing 20.6 and enter the same information as I did to the executable 20L05.exe.

Comments

Related Items

মডুলার C প্রোগ্রামিং (Modular C Programming)

কেবল মাত্র একটি ফাংশন দিয়ে কোনো বড়ো জটিল সমস্যা সমাধানের চেষ্টা করা ভাল প্রোগ্রামিংয়ের পদ্ধতি নয়। সঠিক পদ্ধতি হ'ল সমস্যাটিকে কয়েকটি ছোট ছোট এবং সরল টুকরো করে ফেলা যাতে তা আরও বিশদে বোঝা যায় । তারপরে এই ছোট এবং সরল সমস্যাগুলি সমাধান করার জন্য ছোট ছোট ফাংশন ব্লক তৈরি করা এবং পরে সেগুলি নিয়মানুযায়ী সংযোজিত করা ।

Programming Style

Programming Style

In this section, I'd like to briefly highlight some points that will help you write clean programs that can easily be read, understood, and maintained.

Exercises : Answer the following Question

To help solidify your understanding of this hour's lesson, you are encouraged to answer the quiz questions and finish the exercises provided in the Workshop before you move to the next lesson.

Question and Answer

    Q Is the C preprocessor part of the C compiler?

    A No. The C preprocessor is not part of the C compiler. With its own line-oriented grammar and syntax, the C preprocessor runs before the compiler in order to handle named constants, macros, and inclusion of files.

Compiling Your Code Under Conditions

Compiling Your Code Under Conditions

You can select portions of your C program that you want to compile by using a set of preprocessor directives. This is useful, especially when you're testing a piece of new code or debugging a portion of code.