char *p_message;
p_message = "Teach Yourself C In 21 Days";

You can do the same thing with pointers to type char that are structure members:

struct msg {
    char *p1;
    char *p2;
} myptrs;
myptrs.p1 = "Teach Yourself C In 21 Days";
myptrs.p2 = "By SAMS Publishing";

Figure 11.4 illustrates the result of executing these statements. Each pointer member of the structure points to the first byte of a string, stored elsewhere in memory. Contrast this with Figure 11.3, which shows how data is stored in a structure that contains arrays of type char.

You can use pointer structure members anywhere a pointer can be used. For example, to print the pointed-to strings, you would write

printf("%s %s", myptrs.p1, myptrs.p2);

Figure 11.4. A structure that contains pointers to type char.

What's the difference between using an array of type char as a structure member and using a pointer to type char? These are both methods for "storing" a string in a structure, as shown here in the structure msg, which uses both methods:

struct msg {
    char p1[30];
    char *p2;
} myptrs;

Recall that an array name without brackets is a pointer to the first array element. Therefore, you can use these two structure members in similar fashion:

strcpy(myptrs.p1, "Teach Yourself C In 21 Days");
strcpy(myptrs.p2, "By SAMS Publishing");
/* additional code goes here */
puts(myptrs.p1);
puts(myptrs.p2);

What's the difference between these methods? It is this: If you define a structure that contains an array of type char, every instance of that structure type contains storage space for an array of the specified size. Furthermore, you're limited to the specified size; you can't store a larger string in the structure. Here's an example:

struct msg {
    char p1[10];
    char p2[10];
} myptrs;
...
strcpy(p1, "Minneapolis"); /* Wrong! String longer than array.*/
strcpy(p2, "MN");          /* OK, but wastes space because    */
                           /* string shorter than array.      */

If, on the other hand, you define a structure that contains pointers to type char, these restrictions don't apply. Each instance of the structure contains storage space for only the pointer. The actual strings are stored elsewhere in memory (but you don't need to worry about where in memory). There's no length restriction or wasted space. The actual strings aren't stored as part of the structure. Each pointer in the structure can point to a string of any length. That string becomes part of the structure, even though it isn't stored in the structure.

Pointers to Structures

A C program can declare and use pointers to structures, just as it can declare pointers to any other data storage type. As you'll see later in this chapter, pointers to structures are often used when passing a structure as an argument to a function. Pointers to structures are also used in a very powerful data storage method known as linked lists. Linked lists are explored on Day 15, "Pointers: Beyond the Basics."

For now, take a look at how your program can create and use pointers to structures. First, define a structure:

struct part {
    int number;
    char name[10];
};

Now declare a pointer to type part:

struct part *p_part;

Remember, the indirection operator (*) in the declaration says that p_part is a pointer to type part, not an instance of type part.

Can the pointer be initialized now? No, because even though the structure part has been defined, no instances of it have been declared. Remember that it's a declaration, not a definition, that sets aside storage space in memory for a data object. Because a pointer needs a memory address to point to, you must declare an instance of type part before anything can point to it. Here's the declaration:

struct part gizmo;

Now you can perform the pointer initialization:

p_part = &gizmo;

This statement assigns the address of gizmo to p_part. (Recall the address-of operator, &, from Day 9.) Figure 11.5 shows the relationship between a structure and a pointer to the structure.

Figure 11.5. A pointer to a structure points to the structure's first byte.

Now that you have a pointer to the structure gizmo, how do you make use of it? One method uses the indirection operator (*). Recall from Day 9 that if ptr is a pointer to a data object, the expression *ptr refers to the object pointed to.

Applying this to the current example, you know that p_part is a pointer to the structure gizmo, so *p_part refers to gizmo. You then apply the structure member operator (.) to access individual members of gizmo. To assign the value 100 to gizmo.number, you could write

(*p_part).number = 100;

*p_part must be enclosed in parentheses because the (.) operator has a higher precedence than the (*) operator.

A second way to access structure members using a pointer to the structure is to use the indirect membership operator, which consists of the characters -> (a hyphen followed by the greater-than symbol). (Note that when they are used together in this way, C treats them as a single operator, not two.) This symbol is placed between the pointer name and the member name. For example, to access the number member of gizmo with the p_part pointer, you would write

p_part->number

Looking at another example, if str is a structure, p_str is a pointer to str, and memb is a member of str, you can access str.memb by writing

p_str->memb

Therefore, there are three ways to access a structure member:

If p_str is a pointer to the structure str, the following expressions are all equivalent:

str.memb
(*p_str).memb
p_str->memb

Pointers and Arrays of Structures

You've seen that arrays of structures can be a very powerful programming tool, as can pointers to structures. You can combine the two, using pointers to access structures that are array elements.

To illustrate, here is a structure definition from an earlier example:

struct part {
    int number;
    char name[10];
};

After the part structure is defined, you can declare an array of type part:

struct part data[100];

Next you can declare a pointer to type part and initialize it to point to the first structure in the array data:

struct part *p_part;
p_part = &data[0];

Recall that the name of an array without brackets is a pointer to the first array element, so the second line could also have been written as

p_part = data;

You now have an array of structures of type part and a pointer to the first array element (that is, the first structure in the array). For example, you could print the contents of the first element using the statement

printf("%d %s", p_part->number, p_part->name);

What if you wanted to print all the array elements? You would probably use a for loop, printing one array element with each iteration of the loop. To access the members using pointer notation, you must change the pointer p_part so that with each iteration of the loop it points to the next array element (that is, the next structure in the array). How do you do this?

C's pointer arithmetic comes to your aid. The unary increment operator (++) has a special meaning when applied to a pointer: It means "increment the pointer by the size of the object it points to." Put another way, if you have a pointer ptr that points to a data object of type obj, the statement

ptr++;

has the same effect as

ptr += sizeof(obj);

This aspect of pointer arithmetic is particularly relevant to arrays because array elements are stored sequentially in memory. If a pointer points to array element n, incrementing the pointer with the (++) operator causes it to point to element n + 1. This is illustrated in Figure 11.6, which shows an array named x[] that consists of four-byte elements (for example, a structure containing two type int members, each two bytes long). The pointer ptr was initialized to point to x[0]; each time ptr is incremented, it points to the next array element.

Figure 11.6. With each increment, a pointer steps to the next array element.

What this means is that your program can step through an array of structures (or an array of any other data type, for that matter) by incrementing a pointer. This sort of notation is usually easier to use and more concise than using array subscripts to perform the same task. Listing 11.4 shows how you do this.

Listing 11.4. Accessing successive array elements by incrementing a pointer.

1:  /* Demonstrates stepping through an array of structures */
2:  /* using pointer notation. */
3:
4:  #include <stdio.h>
5:
6:  #define MAX 4
7:
8:  /* Define a structure, then declare and initialize */
9:  /* an array of four structures. */
10:
11: struct part {
12:     int number;
13:     char name[10];
14: } data[MAX] = {1, "Smith",
15:                2, "Jones",
16:                3, "Adams",
17:                4, "Wilson"
18:                };
19:
20: /* Declare a pointer to type part, and a counter variable. */
21:
22: struct part *p_part;
23: int count;
24:
25: main()
26: {
27:     /* Initialize the pointer to the first array element. */
28:
29:     p_part = data;
30:
31:     /* Loop through the array, incrementing the pointer */
32:     /* with each iteration. */
33:
34:     for (count = 0; count < MAX; count++)
35:     {
36:         printf("At address %d: %d %s\n", p_part, p_part->number,
37:                 p_part->name);
38:         p_part++;
39:     }
40:
41:     return 0;
42: }
At address 96: 1 Smith
At address 108: 2 Jones
At address 120: 3 Adams
At address 132: 4 Wilson

ANALYSIS: First, in lines 11 through 18, this program declares and initializes an array of structures called data. A pointer called p_part is then defined in line 22 to be used to point to the data structure. The main() function's first task in line 29 is to set the pointer, p_part, to point to the part structure that was declared. All the elements are then printed using a for loop in lines 34 through 39 that increments the pointer to the array with each iteration. The program also displays the address of each element.

Look closely at the addresses displayed. The precise values might differ on your system, but they are in equal-sized increments--just the size of the structure part (most systems will have an increment of 12). This clearly illustrates that incrementing a pointer increases it by an amount equal to the size of the data object it points to.

Passing Structures as Arguments to Functions

Like other data types, a structure can be passed as an argument to a function. Listing 11.5 shows how to do this. This program is a modification of the program shown in Listing 11.2. It uses a function to display data on the screen, whereas Listing 11.2 uses statements that are part of main().

Listing 11.5. Passing a structure as a function argument.

1:  /* Demonstrates passing a structure to a function. */
2:
3:  #include <stdio.h>
4:
5:  /* Declare and define a structure to hold the data. */
6:
7:  struct data{
8:      float amount;
9:      char fname[30];
10:     char lname[30];
11: } rec;
12:
13: /* The function prototype. The function has no return value, */
14: /* and it takes a structure of type data as its one argument. */
15:
16: void print_rec(struct data x);
17:
18: main()
19: {
20:     /* Input the data from the keyboard. */
21:
22:     printf("Enter the donor's first and last names,\n");
23:     printf("separated by a space: ");
24:     scanf("%s %s", rec.fname, rec.lname);
25:
26:     printf("\nEnter the donation amount: ");
27:     scanf("%f", &rec.amount);
28:
29:     /* Call the display function. */
30:     print_rec( rec );
31:
32:     return 0;
33: }
34: void print_rec(struct data x)
35: {
36:     printf("\nDonor %s %s gave $%.2f.\n", x.fname, x.lname,
37:             x.amount);
38: }
Enter the donor's first and last names,
separated by a space: Bradley Jones
Enter the donation amount: 1000.00
Donor Bradley Jones gave $1000.00.

ANALYSIS: Looking at line 16, you see the function prototype for the function that is to receive the structure. As you would with any other data type that was going to be passed, you need to include the proper arguments. In this case, it is a structure of type data. This is repeated in the header for the function in line 34. When calling the function, you only need to pass the structure instance name--in this case, rec (line 30). That's all there is to it. Passing a structure to a function isn't very different from passing a simple variable.

You can also pass a structure to a function by passing the structure's address (that is, a pointer to the structure). In fact, in older versions of C, this was the only way to pass a structure as an argument. It's not necessary now, but you might see older programs that still use this method. If you pass a pointer to a structure as an argument, remember that you must use the indirect membership operator (->) to access structure members in the function.


DON'T confuse arrays with structures!

DO take advantage of declaring a pointer to a structure--especially when using arrays of structures.

DON'T forget that when you increment a pointer, it moves a distance equivalent to the size of the data to which it points. In the case of a pointer to a structure, this is the size of the structure.

DO use the indirect membership operator (->) when working with a pointer to a structure.


Unions

Unions are similar to structures. A union is declared and used in the same ways that a structure is. A union differs from a structure in that only one of its members can be used at a time. The reason for this is simple. All the members of a union occupy the same area of memory. They are laid on top of each other.

Defining, Declaring, and Initializing Unions

Unions are defined and declared in the same fashion as structures. The only difference in the declarations is that the keyword union is used instead of struct. To define a simple union of a char variable and an integer variable, you would write the following:

union shared {
    char c;
    int i;
};

This union, shared, can be used to create instances of a union that can hold either a character value c or an integer value i. This is an OR condition. Unlike a structure that would hold both values, the union can hold only one value at a time. Figure 11.7 illustrates how the shared union would appear in memory.

A union can be initialized on its declaration. Because only one member can be used at a time, only one can be initialized. To avoid confusion, only the first member of the union can be initialized. The following code shows an instance of the shared union being declared and initialized:

union shared generic_variable = {`@'};

Notice that the generic_variable union was initialized just as the first member of a structure would be initialized.

Figure 11.7. The union can hold only one value at a time.

Accessing Union Members

Individual union members can be used in the same way that structure members can be used--by using the member operator (.). However, there is an important difference in accessing union members. Only one union member should be accessed at a time. Because a union stores its members on top of each other, it's important to access only one member at a time. Listing 11.6 presents an example.

Listing 11.6. An example of the wrong use of unions.

1:   /* Example of using more than one union member at a time */
2:   #include <stdio.h>
3:
4:   main()
5:   {
6:       union shared_tag {
7:           char   c;
8:           int    i;
9:           long   l;
10:          float  f;
11:          double d;
12:      } shared;
13:
14:      shared.c = `$';
15:
16:      printf("\nchar c   = %c",  shared.c);
17:      printf("\nint i    = %d",  shared.i);
18:      printf("\nlong l   = %ld", shared.l);
19:      printf("\nfloat f  = %f",  shared.f);
20:      printf("\ndouble d = %f",  shared.d);
21:
22:      shared.d = 123456789.8765;
23:
24:      printf("\n\nchar c   = %c",  shared.c);
25:      printf("\nint i    = %d",  shared.i);
26:      printf("\nlong l   = %ld", shared.l);
27:      printf("\nfloat f  = %f",  shared.f);
28:      printf("\ndouble d = %f\n",  shared.d);
29:
30:      return 0;
31:  }
char c   = $
int i    = 4900
long l   = 437785380
float f  = 0.000000
double d = 0.000000
char c   = 7
int i    = -30409
long l   = 1468107063
float f  = 284852666499072.000000
double d = 123456789.876500

ANALYSIS: In this listing, you can see that a union named shared is defined and declared in lines 6 through 12. shared contains five members, each of a different type. Lines 14 and 22 initialize individual members of shared. Lines 16 through 20 and 24 through 28 then present the values of each member using printf() statements.

Note that, with the exceptions of char c = $ and double d = 123456789.876500, the output might not be the same on your computer. Because the character variable, c, was initialized in line 14, it is the only value that should be used until a different member is initialized. The results of printing the other union member variables (i, l, f, and d) can be unpredictable (lines 16 through 20). Line 22 puts a value into the double variable, d. Notice that the printing of the variables again is unpredictable for all but d. The value entered into c in line 14 has been lost because it was overwritten when the value of d in line 22 was entered. This is evidence that the members all occupy the same space.

The union Keyword

union tag {
    union_member(s);
    /* additional statements may go here */
}instance;

The union keyword is used for declaring unions. A union is a collection of one or more variables (union_members) that have been grouped under a single name. In addition, each of these union members occupies the same area of memory.

The keyword union identifies the beginning of a union definition. It's followed by a tag that is the name given to the union. Following the tag are the union members enclosed in braces. An instance, the actual declaration of a union, also can be defined. If you define the structure without the instance, it's just a template that can be used later in a program to declare structures. The following is a template's format:

union tag {
    union_member(s);
    /* additional statements may go here */
};

To use the template, you would use the following format:

union tag instance;

To use this format, you must have previously declared a union with the given tag.

Example 1

/* Declare a union template called tag */
union tag {
    int nbr;
    char character;
}
/* Use the union template */
union tag mixed_variable;

Example 2

/* Declare a union and instance together */
union generic_type_tag {
    char c;
    int i;
    float f;
    double d;
} generic;

Example 3

/* Initialize a union. */
union date_tag {
    char full_date[9];
    struct part_date_tag {
        char month[2];
        char break_value1;
        char day[2];
        char break_value2;
        char year[2];
    } part_date;
}date = {"01/01/97"};

Listing 11.7 shows a more practical use of a union. Although this use is simplistic, it's one of the more common uses of a union.

Listing 11.7. A practical use of a union.

1:   /* Example of a typical use of a union */
2:
3:   #include <stdio.h>
4:
5:   #define CHARACTER   `C'
6:   #define INTEGER     `I'
7:   #define FLOAT       `F'
8:
9:   struct generic_tag{
10:      char type;
11:      union shared_tag {
12:          char   c;
13:          int    i;
14:          float  f;
15:      } shared;
16:  };
17:
18:  void print_function( struct generic_tag generic );
19:
20:  main()
21:  {
22:      struct generic_tag var;
23:
24:      var.type = CHARACTER;
25:      var.shared.c = `$';
26:      print_function( var );
27:
28:      var.type = FLOAT;
29:      var.shared.f = (float) 12345.67890;
30:      print_function( var );
31:
32:      var.type = `x';
33:      var.shared.i = 111;
34:      print_function( var );
35:      return 0;
36:  }
37:  void print_function( struct generic_tag generic )
38:  {
39:      printf("\n\nThe generic value is...");
40:      switch( generic.type )
41:      {
42:          case CHARACTER: printf("%c",  generic.shared.c);
43:                          break;
44:          case INTEGER:   printf("%d",  generic.shared.i);
45:                          break;
46:          case FLOAT:     printf("%f",  generic.shared.f);
47:                          break;
48:          default:        printf("an unknown type: %c\n",
49:                                  generic.type);
50:                          break;
51:      }
52:  }
The generic value is...$
The generic value is...12345.678711
The generic value is...an unknown type: x

ANALYSIS: This program is a very simplistic version of what could be done with a union. This program provides a way of storing multiple data types in a single storage space. The generic_tag structure lets you store either a character, an integer, or a floating-point number within the same area. This area is a union called shared that operates just like the examples in Listing 11.6. Notice that the generic_tag structure also adds an additional field called type. This field is used to store information on the type of variable contained in shared. type helps prevent shared from being used in the wrong way, thus helping to avoid erroneous data such as that presented in Listing 11.6.

A formal look at the program shows that lines 5, 6, and 7 define constants CHARACTER, INTEGER, and FLOAT. These are used later in the program to make the listing more readable. Lines 9 through 16 define a generic_tag structure that will be used later. Line 18 presents a prototype for the print_function(). The structure var is declared in line 22 and is first initialized to hold a character value in lines 24 and 25. A call to print_function() in line 26 lets the value be printed. Lines 28 through 30 and 32 through 34 repeat this process with other values.

The print_function() is the heart of this listing. Although this function is used to print the value from a generic_tag variable, a similar function could have been used to initialize it. print_function() will evaluate the type variable in order to print a statement with the appropriate variable type. This prevents getting erroneous data such as that in Listing 11.6.


DON'T try to initialize more than the first union member.

DO remember which union member is being used. If you fill in a member of one type and then try to use a different type, you can get unpredictable results.

DON'T forget that the size of a union is equal to its largest member.

DO note that unions are an advanced C topic.


typedef and Structures

You can use the typedef keyword to create a synonym for a structure or union type. For example, the following statements define coord as a synonym for the indicated structure:

typedef struct {
    int x;
    int y;
} coord;

You can then declare instances of this structure using the coord identifier:

coord topleft, bottomright;

Note that a typedef is different from a structure tag, as described earlier in this chapter. If you write

struct coord {
    int x;
    int y;
};

the identifier coord is a tag for the structure. You can use the tag to declare instances of the structure, but unlike with a typedef, you must include the struct keyword:

struct coord topleft, bottomright;

Whether you use typedef or a structure tag to declare structures makes little difference. Using typedef results in slightly more concise code, because the struct keyword doesn't need to be used. On the other hand, using a tag and having the struct keyword explicit makes it clear that it is a structure being declared.

Summary

This chapter showed you how to use structures, a data type that you design to meet the needs of your program. A structure can contain any of C's data types, including other structures, pointers, and arrays. Each data item within a structure, called a member, is accessed using the structure member operator (.) between the structure name and the member name. Structures can be used individually, and they can also be used in arrays.

Unions were presented as being similar to structures. The main difference between a union and a structure is that the union stores all its members in the same area. This means that only one member of a union can be used at a time.

Q&A

Q Is there any reason to declare a structure without an instance?

A I showed you two ways of declaring a structure. The first was to declare a structure body, tag, and instance all at once. The second was to declare a structure body and tag without an instance. An instance can then be declared later by using the struct keyword, the tag, and a name for the instance. It's common programming practice to use the second method. Many programmers declare the structure body and tag without any instances. The instances are then declared later in the program. The next chapter describes variable scope. Scope will apply to the instance, but not to the tag or structure body.

Q Is it more common to use a typedef or a structure tag?

A Many programmers use typedefs to make their code easier to read, but it makes little practical difference. Many add-in libraries that contain functions are available for purchase. These add-ins usually have a lot of typedefs to make the product unique. This is especially true of database add-in products.

Q Can I simply assign one structure to another with the assignment operator?

A Yes and no. Newer versions of C compilers let you assign one structure to another, but older versions might not. In older versions of C, you might need to assign each member of the structures individually! This is true of unions also.

Q How big is a union?

A Because each of the members in a union is stored in the same memory location, the amount of room required to store the union is equal to that of its largest member.

Workshop

The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned.

Quiz

1. How is a structure different from an array?

2. What is the structure member operator, and what purpose does it serve?

3. What keyword is used in C to create a structure?

4. What is the difference between a structure tag and a structure instance?

5. What does the following code fragment do?

struct address {
    char name[31];
    char add1[31];
    char add2[31];
    char city[11];
    char state[3];
    char zip[11];
} myaddress = { "Bradley Jones",
                "RTSoftware",
                "P.O. Box 1213",
                "Carmel", "IN", "46032-1213"};


6. Assume you have declared an array of structures and that ptr is a pointer to the first array element (that is, the first structure in the array). How would you change ptr to point to the second array element?

Exercises

1. Write code that defines a structure named time, which contains three int members.

2. Write code that performs two tasks: defines a structure named data that contains one type int member and two type float members, and declares an instance of type data named info.

3. Continuing with exercise 2, how would you assign the value 100 to the integer member of the structure info?

4. Write code that declares and initializes a pointer to info.

5. Continuing with exercise 4, show two ways of using pointer notation to assign the value 5.5 to the first float member of info.

6. Write the definition for a structure type named data that can hold a single string of up to 20 characters.

7. Create a structure containing five strings: address1, address2, city, state, and zip. Create a typedef called RECORD that can be used to create instances of this structure.

8. Using the typedef from exercise 7, allocate and initialize an element called myaddress.

9. BUG BUSTER: What is wrong with the following code?

struct {
    char zodiac_sign[21];
    int month;
} sign = "Leo", 8;


10. BUG BUSTER: What is wrong with the following code?

/* setting up a union */
union data{
    char a_word[4];
    long a_number;
}generic_variable = { "WOW", 1000 };


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.