Introduction to Structures

Structures are not a data type in themselves but rather a way for you to define your own data type. Moreover, with structures you can better model real-world objects. For example, if you wanted to create a list of books, storing the author, publication date, and title of each, you could do that with arrays: one array for the authors, one for the publication years, and a third for the titles (Figure 13.1). But such a construct would be less than ideal, at the very least because the three arrays would be seemingly unrelated. A better solution is to create one type of variable that can store the author's name, the title, and the publication date, all under one umbrella. This is where structures come in.

Figure 13.1. Using arrays, all of the required information is stored but the association between the records isn't clear.


To use structures, you first define the structure, using the data types with which you are already familiar. The syntax requires the struct keyword:

struct structure_name {
   var_type member_var1;
   var_type member_var2;
};

For the books example, you can create a structure like this:

struct books {
   char author[40];
   char title[60];
   int pub_year;
};

The author, title, and pub_year variables are now all members of the books structure.

Once you've defined the structure model, you can create variables of that type:

struct structure_name variable_name;
struct books my_book;

To refer to a specific structure member, use the variable_name.member syntax:

my_book.pub_year = 2005;

In this next example, the record_weather application from Chapter 12, “File Input and Output,” will be rewritten to use structures. Each structure will then be written to a binary file.

To use structures

1.
Create a new file or project in your text editor or IDE.

2.
Type the standard beginning lines of code (Script 13.1):

/* weather_structure.c - Script 13.1
 */

#include <stdio.h>

Script 13.1. This new version of the record_weather application uses structures to create records. One structure at a time is then written to the binary file.


3.
Set the file path and name as a C preprocessor macro:

#define THEFILE
 "/Users/larry/Desktop/weather.dat"

or

#define THEFILE "C:\Documents and
 Settings\Larry Ullman\Desktop\
 weather.dat"

The exact syntax will depend on your operating system, but you should use an absolute path to the file here. Also, a .dat file extension is being used—since it will be a binary data file—instead of .txt.

4.
Begin the main function:

int main (void) {

5.
Define the structure and create a variable of that type:

struct weather_record {
     char date[11];
     int high;
     int low;
};
struct weather_record today;

This structure will be called weather_record. It contains three members: a character array called date, an integer called high, and another integer called low. Each of these correspond to the variables that were used in the previous version of this application.

One variable of the weather_record type is created, called today. Note that you have to use both the keyword struct as well as the structure name in defining this variable.

6.
Create a file pointer and open the file for appended binary writing:

FILE *fp;
fp = fopen(THEFILE, "ab");

The fp file pointer is being used exactly as in the previous chapter. The second line opens the file in binary mode for appended writing. Each record written to the file will be added after any existing data.

7.
Start a conditional based on the file pointer and prompt the user:

if (fp != NULL) {
     printf ("Enter a date, the high
 temperature, and the low
 temperature.
(YYYY-MM-DD ## ##):
 ");

The prompt (Figure 13.2) is exactly like the prompt used before, indicating what information is expected, in what order, and in what format.

Figure 13.2. Prompting the user to enter a day's date, and high and low temperatures.


8.
Read in the keyed input as part of a conditional, assigning each value to the appropriate structure field:

if (scanf ("%10s %d %d", today.date,
&today.high, &today.low) == 3) {

Here the scanf() function is working as you've seen it before: reading in a string and two integers, and assigning them to variables. To refer to the structure's members, the var.members syntax is used, so today.date refers to today's date field. The number fields are preceded by the address of operator (&), in keeping with the proper scanf() syntax.

This conditional checks the value returned by scanf() to see if it's equal to 3, meaning that the data was properly entered.

9.
Write the structure to the binary file and print a message to the user:

fwrite (&today, sizeof(struct
 weather_record), 1, fp);
printf ("The data has been
 written.
");

The fwrite() line starts by using the &today variable as its pointer. This is necessary since fwrite() expects an address in memory (where it will find the data to be written) as its first argument. The function is then told to write 1 block of data, which is sizeof(struct weather_record) bytes in size. In other words, take 1 weather_record structure's worth of data from the memory and write it to the file.

The printf() line just gives you something to see when the application runs. You could also print out the entered values, if you want (Figure 13.3), by using

printf("You entered the date as %s,
 the high temperature as %d, and
 the low temperature as %d.
",
 today.date, today.high, today.low);

Figure 13.3. You can change the responsive print statement (line 35 of Script 13.1) to be more informative, if you desire.


10.
Complete the scanf() conditional:

} else {
    printf ("The data was not in
 the proper format.
");
}

11.
Complete the fp conditional:

} else {
    printf ("The file could not
 be opened.
");
    return 1;
}

12.
Close the file:

if (fclose(fp) != 0) {
    printf ("The file could not
 be closed.
");
}

13.
Complete the main function:

    getchar();
    getchar();
    return 0;
}

14.
Save the file as weather_structure.c, compile, and debug as necessary.

15.
Run the application, populating the data file (Figures 13.4 and 13.5).

Figure 13.4. The end user cannot tell what goes on behind the scenes (assuming everything works) but this version of the application now uses structures.


Figure 13.5. Entering another record.


✓ Tips

  • You can define a specific structure variable during the declaration of the structure:

    struct student {
         char name[30];
         float gpa;
    } timmy;
    
  • As with other variables, the value of a structure can be initialized during the declaration:

    struct student {
         char name[30];
         float gpa;
    } timmy = { "Timmy Noonan", 3.65};
    
  • You cannot use an existing keyword as your structure or variable name.


A Look at Unions

A union is somewhat like a structure in its flexible design but can contain only a single value at a time. It can be defined to hold many types of values, but only one of those values will ever be stored.

For example, if you wanted to define a variable that would store one type of identification—mother's maiden name, user's date of birth, or the last four digits of the user's social security number—you can use a union:

union id {
   char maiden[20];
   char dob[11];
   int ssn;
};

Once you've defined the union, you can create variables of that type:

union id person; 

The union variable can now be assigned values like a structure, using the variable.member syntax:

strncpy(person.maiden, "Malawey", 7);

The person union now stores the value Malawey in the maiden field.

person.ssn = 1234;

The person union now stores the value 1234 in the ssn field and the maiden field no longer has a value.

As you can tell from this example, unions have limited use but they can definitely come in handy under the right circumstances.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset