Previous: 10.4 An Example - Payroll Data Records
Up: 10 Sorting and Searching
Next: 10.6 Common Errors
Previous Page: 10.4 An Example - Payroll Data Records
Next Page: 10.6 Common Errors
Very often in programs, a generic operation must be performed on
data of different types.
For example, in our bubble sort algorithm for the payroll records,
when elements were found out of order in the id[] array,
we needed to swap the integer elements in that array as well as
the float elements in the hrs[] and rate[] arrays.
If we decided to implement this swapping operation as a function,
we would need to write two functions: one to swap integers, and
another to swap floating point values; even though the algorithm
for swapping is the same in both cases.
(We wrote a swap function for integers
using pointers in Chapter ).
The C language provides a mechanism which allows us to write a single swapping function which can be used on any data type. This mechanism is called a polymorphic data type, i.e. a data type which can be transformed to any distinct data type as required. An item of polymorphic data type is created by the use of a generic pointer. A generic pointer is simply a byte address without an associated type. In other words, a generic pointer does not point to an object of a specific type; it just points to some location in the memory of the computer. In ANSI C, a generic pointer is declared as a void pointer (in old C, a generic pointer is a char pointer). It is only when the actual operations must be performed on the data that generic pointers are cast to pointers to specific types and dereferenced.
Using the concept of a generic pointer, we can assume the following prototype for a function to swap two data items of any type:
void gen_swap(void * x, void * y, char type);
Here, x and y are generic pointers to two data items, and type specifies the type of the data using a single character. With this information, we can now rewrite the function, sortdata(), in the file, payutil.c using gen_swap to swap all data items. The code is shown in Figure 10.21. Notice in the calls to gen_swap() we cast the pointers to the integer array elements ( id + k and id + k + 1) to void pointers. Similarly, the pointers to the float data items in the hrs[] and rate[] arrays are cast to void pointers. We pass the character constants 'd' for integer, or 'f' for float to tell gen_swap() the type of the data it is to swap.
We can now write the code for gen_swap() as seen in Figure 10.22. We have declared two temporary variables, temp and ftmp to hold an integer or float value, respectively when we do the swapping. The variable, type is used to switch to the appropriate code sequence to swap the two data items.
If we made these modifications to payutil.c and recompiled our program, it would behave exactly as it did in the last section. Of course, as we stated earlier, we may not want to use a function to perform the swap in bubble sort because of the overhead in calling and returning from a function. As another example of the use of the polymorphic data type, consider writing a function that will print an array, regardless of type, with five elements per line. We may have an array of integers and an array of floats to be printed and wish to use the same function to format the lines of output. Figure 10.23 shows a driver program and the function, praray().
The function calls to praray() pass the array pointers after first casting them to generic pointer types. In the function, praray(), we use the array index to determine when a new line is needed. Since we wish to print five elements to a line, a newline is printed every time the index, i, is a multiple of 6, i.e. i % 6 is zero. When the index i is zero, no newline is needed.
The void pointer, y,
points to the array and the type value is a character, 'd' for
integers, and 'f' for float, as before.
Each element of the array is printed by means
of a switch statement.
The switch cases are selected by the type of the array
passed. If the type is 'd',
a decimal integer is printed; if the type is
'f', a float is printed.
If desired, the function can be extended to handle
other types as well. Let us examine the printing of an element of an
integer array. For a type 'd',
the argument expression in printf() is:
*((int *) y + i)
The void pointer, y, is first cast to the desired type, i.e.
int *; then, the
int * is increased by i so as to point to the
element of an integer array.
This pointer is finally dereferenced to access the
element of the array.
Thus, printf() prints the value of the
element of an integer array.
Similarly, a float array element is printed out by first casting the generic
pointer to a float pointer. A sample output is shown below.
Use of polymorphic data types makes for compact programs; however, their use is not recommended for beginning programmers. For the most part, we will not use them in this text.