This month Steve Mumford investigates some alternative methods of data storage.
Up till now, if we've wanted to store a set of related data items such as names or telephone numbers, we've used arrays - David described how to set them up back in the December 1994 issue. As a brief reminder, here's a few examples of array declarations:
float numbers[100]; int grid[5][10]; char names[10][25]; char *places[20];
It's important to remember that when you declare your arrays, you're specifying the maximum number of elements you'll be using - in the first example, you've defined ten 'cells' starting at numbers[0] and goint to numbers[9].
Arrays are quite versatile, but they do have their limitations. If you're writing a simple database to store the contents of your address book, you'd need several arrays to hold the different types of information - one for the names, another for the telephone numbers, and so on. In order to perform an operation on an individual record, you have to examine each array in turn and manually reconstitute the information. It's simple enough to do, but when each record contains a lot of data fields, it quickly becomes tiresome, especiallyfor routine operations like copying one record to another.
Another consequence of arranging the data in separate arrays according to its type is that the natural structure of information is lost - in an address book, all the information for one address is stored in one space; you'd never dream of storing all the phone numbers in a separate chapter and cross-referencing them to the names whenever you wanted to call someone. It's this philosophy that brought about a mjor addition to the language of C, and it's this addition that I'll be looking at next.
struct mode { char name[15]; int xres; int yres; int colours; };
Essentially, you put together a list of the items you want the structure to hold, and initialise them as if they were separate variables - the only difference is that you surround the whole lot in braces at the end of a struct command - and since it's a command we're dealing with we need to include the semicolon after the closing brace. The example above assigns the name mode as a new variable type. No structures have been created in memory yet; we've just told the compiler how to make one when it needs to. The structure mode contains four elements - a block of 15 bytes in which a name can be stored, the x and y resolutions of the mode, and finally the number of colours. In order to declare a structure of this type that we can use, we need to do the following:
struct mode games;
or
struct mode hires;
It's similar to declaring a variable of any other type, but because mode is a type we've just created - a pseudotype, if you like - we need to remind the compiler of that fact by prefixing it with the struct keyword.
If you're going to be declaring several structures with the same definition, it's command practice to use the typedef command to cut down on the typing. The following example instructs the compiler to treat any MODE instruction it finds as if it were struct mode.
typedef struct mode MODE; MODE games;
You can even use the typedef command at the same time as the definition of the structure, which accounts for why you'll see some pretty big differences between structure definitions.
typedef struct { int numerator; int denominator; } FRACTION; FRACTION frac1;
So, you've defined and declared your structure, so how do you use it? In the same way that you use an index to look at different elements in an array, you use the names of the members of the structure to access them, by using the dot operator.
gets(games.name); games.xres = 640; games.yres = 256; games.colours = 256;
In the example above, I'm storing information in the structure named games by adding the appropriate element onto the end of the structure name.
MODE modes[10];
The above statement declares an array of 10 mode structures, and each one can contain a name, the x and y resolutions, and the number of colours as before.
A structure in an array is accessed in almost the same way as before, using the dot operator. The following example steps through the array of structures and reads in a name for each of them.
for (x=0; x<10; x++) { printf("Please enter the name for mode %d:\n", x); gets(mode[x].name); }
This allows use to store our list of names and addresses in one large array, and operations such as copying, sorting or deleting records are made much easier by the fact that we can access a whole chunk of data in one go. The code fragment belows creates a new structure named chosen and copies the fourth structure from the modes array into it.
MODE chosen; chosen = modes[3];
Note that this copies the array of characters in modes[3].name, which would have needed the strcpy() function outside a structure.
Firstly, you're not limited to including single variables in structures; you can put arrays within them too. If you want to progress further, you can also include other structures, arrays of structures - the list goes on. There are limitations; if you're defining a structure named address, you're not allowed to refer to structures of that type within its own definition. It's mostly because you fall into a recursion trap - the compiler would try to put the address structure inside itself, and get confused. However, as with most limitations, there's a way round it, and this leads on to such marvels as linked lists and binary trees - more on which next month.
struct example { int array[10][20]; MODE games; MODE strucarray[5]; }; struct example ex1;
Once this rather complex structure shown above has been set up, you can access all the elements within it following the rules laid out earlier. All the following assignments are possible.
ex1.array[5][5] = 10; ex1.games.xres = 640; ex1.strucarray[3].colours = 16;
typedef struct { int x; float y; } SIMPLE; SIMPLE *var1;
The above fragment defines a structure and creates the variable type SIMPLE for it. The last line declares a pointer to a structure of type SIMPLE, but doesn't allocate any memory yet. That's done by using the malloc() function, and the sizeof() function calculates the amount of memory the structure takes up. If it's a more complicated structure, you might have to determine its size for yourself.
var1 = malloc(sizeof(SIMPLE));
You can't access these structures in quite the same way as the ones mentioned earlier in the column, but a special operator is provided to avoid all those stars and ampersands that are normally associated with pointers. I'll cover that next month, so I'll see you then.
Source: | Acorn User - 158 - August 1995 |
Publication: | Acorn User |
Contributor: | Steve Mumford |