Let us look at another example of file modularization of a program. Consider the program of Section 3.6.1 that reads in, creates, and prints the information fields of each record on a list of stock records. That version is in a single file and is functionally modularized. The data abstraction list, with its operations next, setlink, setinfo, avail, getnextrecord, printrecord, and anotherrecord, appear together.
The version of the program that follows uses a module for the list data abstraction with the header file, list.h, and the operations file, list.c. The file, list.c, and the program file, listexample.c, are compiled separately.
The file list.h:
The File list.h
/* header for list */
#define MAXSIZE 5
#define NULL 0
#define SENTINEL -1
typedef struct
{
int month;
int day;
int year;
}date;
typedef struct
{
char name[MAXSIZE];
int shares;
float value;
date datebought;
}infofield;
typedef struct listrecord
{
infofield info;
struct listrecord *link;
}stockrecord,*listpointer;
extern listpointer next(/* listpointer */);
/* Returns a copy of listpointer in link field of
record pointed to by listpointer */
extern listpointer setnull();
/* Returns a null listpointer value */
extern setlink(/* listpointer_1,listpointer_2 */);
/* Sets the link field of the record pointed to
by listpointer_1 to the value of
listpointer_2
*/
extern setinfo(/* listpointer_1,listpointer_2 */);
/* Sets the info field of the record pointed to
by listpointer_1 to the contents of the
info field of the record pointed to by
listpointer_2
*/
extern listpointer avail();
/* Returns a pointer to storage for
a record of type struct stockrecord */
extern listpointer getnextrecord();
/* Returns a pointer to a record
containing the next input records'
information, or a null pointer if
the next input is the sentinel
*/
extern printrecord(/* listpointer */);
/* Prints the information in the record
pointed to by listpointer */
extern anotherrecord(/* listpointer */);
/* Returns true if there are no
more records and false otherwise */
The File list.c
/* operations allowed on a list */
#include "list.h"
listpointer next(pointer)
/* Returns a copy of the
link field in the record
pointed to by pointer
*/
listpointer pointer;
{
return (pointer->link);
}
listpointer setnull()
/* Returns a null pointer */
{
return(NULL);
}
setlink(pointer1,pointer2)
/* Sets the link field of the
record pointed to by pointer1
to the contents of pointer2
*/
listpointer pointer1, pointer2;
{
pointer1->link = pointer2;
}
setinfo(pointer1,pointer2)
/* Sets the infofield of the record
pointed to by pointer1 to the
infofield of the record
pointed to by pointer2
*/
listpointer pointer1,pointer2;
{
int i;
for (i=0;i<MAXSIZE;i++)
pointer1->info.name[i] = pointer2->info.name[i];
pointer1->info.shares = pointer2->info.shares;
pointer1->info.value = pointer2->info.value;
pointer1->info.datebought.month = pointer2->info.datebought.
month;
pointer1->info.datebought.day = pointer2->info.datebought.day;
pointer1->info.datebought.year = pointer2->info.datebought.year;
}
listpointer avail()
*/ Returns a pointer to storage
allocated for a record of type
stockrecord
*/
{
listpointer malloc();
return(malloc(sizeof(stockrecord)));
}
anotherrecord(recordpointer)
/* Returns true if there is
another record
*/
listpointer recordpointer;
{
listpointer setnull();
return(recordpointer != setnull());
}
listpointer getnextrecord()
/* Inputs the data for the new
record and returns a pointer
to a record containing the data
in its infofield, or, if there
are no new records, returns
a null pointer
*/
{
listpointer setnull();
static stockrecord nextrecord;
printf(" Enter number of shares \n");
scanf("%d",&(nextrecord.info.shares));
if(nextrecord.info.shares != SENTINEL)
{
printf(" Enter the stock name - less than %d characters\n",
MAXSIZE);
scanf("%s",nextrecord.info.name);
printf(" Enter the price of one share of the stock \n");
scanf("%f",&(nextrecord.info.value));
printf(" Enter the month day year of the stock purchase
\n");
scanf("%d %d %d",&(nextrecord.info.datebought.month),
&(nextrecord.info.datebought.day),
&(nextrecord.info.datebought.year));
return(&nextrecord);
}
else
return(setnull());
}
printrecord(recordpointer)
/* Prints the contents of the infofield
of the record pointed to by recordpointer
*/
listpointer recordpointer;
{
printf(" The stock is %s \n",recordpointer->info.name);
print(" The number of shares is %d \n",recordpointer->info.
shares);
printf(" The value of a share is %f\n",recordpointer->info.
value);
printf(" The date of purchase is %d %d %d\n\n",
recordpointer->info.datebought.month,
recordpointer->info.datebought.day, recordpointer->
info.datebought.year);
}
Finally, here is the program file to create a list and print its contents. It treats the list as a data abstraction and uses file modularization for the data abstraction.
The File listexample.c
#include "list.h"
#include <stdio.h>
#define TRUE 1
#define FALSE 0
main()
/* Inputs a series of stockrecords,
creates a list of the records, and
prints the contents of each list
record's infofield
*/
{
listpointer stocks,recordpointer,next();
int done;
initialize(&stocks);
recordpointer = stocks;
done = FALSE;
while(!done&&anotherrecord(recordpointer))
{
addrecord(&stocks,recordpointer,&done);
recordpointer = next(recordpointer);
}
recordpointer=stocks;
while (anotherrecord(recordpointer))
{
printrecord(recordpointer);
recordpointer = next(recordpointer);
}
}
initialize(plistname)
/* Allocates storage for the first record
and sets listname to point to it
*/
listpointer *plistname;
{
listpointer avail();
*plistname = avail();
}
addrecord(plistname,recordpointer,pdone)
/* Fills in the current record's data, and
allocates storage for the next record and
adds it to the list. If there is no data for
the current record it sets the link field of
the last list record to null, or the list
head, to null, and done to true.
*/
listpointer *plistname,recordpointer;
int *pdone;
{
static listpointer predecessor;
listpointer setnull(),avail(),
getnextrecord(),pointer;
pointer = getnextrecord();
if (pointer != setnull())
{
setinfo(recordpointer,pointer);
setlink(recordpointer,avail());
predecessor = recordpointer;
}
else if (*plistname != recordpointer)
{
setlink(predecessor,setnull());
*pdone = TRUE;
}
else
{
*plistname = setnull();
*pdone = TRUE;
}
}
The files list.h and list.c now embody the list data abstraction. Note that, should the format of the list change, these changes must be reflected in the header file and also in the operations file. In the later file, list.c, the changes would be localized to setinfo, getnextrecord, and printrecord. Instead, were the format to remain the same but the records stored in an array of records rather than the dynamic memory, more extensive changes would be required in both lists.h and list.c.