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.
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.
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. |
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.