Part II • Managing Data in C
memory, usually with a linked list. In this situation, it may (or may not) be that the links are simply arranged in the order that the memory blocks are allocated. When a memory block is freed, it is removed from the linked list.
The example program in this section allocates memory blocks for each record that the user enters. These blocks are pointed to by links.
Disk-Based Lists
When you create a linked list as a disk-based file, the list’s members must be the same size. If your program has different sized members of a single linked list, the best solution is to use a single union to create a single record of the correct size. The size of the union is determined by its largest member, so the members will be the same size.
Double Linked Lists
In a double linked list, each member has a pointer not only to its successor in the list, but also to its predecessor. Figure 10.4 shows how a double linked list is created. Notice that the pointer to the end of the list is mandatory. This pointer is necessary so that the end of the list can be accessed.
Figure 10.4. A double linked list.
Data Management: Sorts, Lists, and Indexes |
C C C |
|
10C |
|
C C C |
|
C C |
Figure 10.5 shows the structure’s list pointers. (Figure 10.5 and Figure 10.4 are the basis for Figures 10.6 through 10.9.)
Figure 10.5. The CUSTOMER structure’s linked list pointers.
You can perform a trick with a double linked list. When you add a member to a double linked list, the program can examine the key fields of the first and last members to determine whether the list should be traveled from the beginning or the end. This increases the program’s performance. (It doesn’t make sense to start at the first member if the new member will be added near the end of the list.)
Listing 10.4, the LINKLIST.C program, demonstrates the use of a double linked list with dynamically allocated members. The program is simple, without much optimization. The program always has sorted access to the items in the list.
Listing 10.4. LINKLIST.C.
/* LINKLIST, written 1992 by Peter D. Hipson
*A double linked list program. This program has
*better error checking than the CDB program.
*To improve the program, make the ZIP code field a
*character field. A character field is better for ZIP
*codes because many non-US ZIP codes also
*contain letters.
*/
continues
Part II • Managing Data in C
Listing 10.4. continued
#include <string.h> #include <ctype.h> #include <stdio.h> #include <process.h> #include <stdlib.h>
#define |
TRUE |
|
1 |
|
#define |
FALSE |
|
(!TRUE) |
#define |
INCREMENT_AMOUNT |
1 |
/* Add one record at a time */ |
#define |
CUSTOMER_RECORD |
1 |
|
#define |
SUPPLIER_RECORD |
2 |
|
/* Define the structure for the customer database. */ |
struct |
_CUSTNAME; |
|
|
typedef |
struct |
_CUSTNAME { |
|
|
int |
nRecordType; |
// |
1 == Customer record. |
struct _CUSTNAME *NextCustomer; // Link to next, or NULL if none |
struct _CUSTNAME *PrevCustomer; // Link to previous, or NULL if none |
char |
szName[61]; |
// |
60 chars for name; 1 for null at end |
char |
szAddr1[61]; |
// |
60 chars for address; 1 for null at end |
char |
szAddr2[61]; |
// |
60 chars for address; 1 for null at end |
char |
szCity[26]; |
// |
25 chars for city; 1 for null at end |
char |
szState[3]; |
// |
2-character state abbrev. plus null |
int |
lZip; |
// |
Print as %5.5ld for leading 0 |
int |
nRecordNumber; |
// |
Which record number? |
double dSalesTotal; |
// Amount the customer has purchased |
} CUSTNAME; |
|
|
|
typedef |
CUSTNAME |
near *NPCUSTNAME; |
typedef |
CUSTNAME |
*PCUSTNAME; |
|
void |
GiveHelp(void); |
|
|
void |
main() |
|
|
|
Data Management: Sorts, Lists, and Indexes
{
FILE *DataFile;
PCUSTNAME |
FirstCustomer = NULL; |
PCUSTNAME |
LastCustomer = NULL; |
PCUSTNAME |
Customer = NULL; |
PCUSTNAME |
TempCustomer = NULL; |
char |
szFileName[257]; |
char |
szBuffer[257]; |
int |
nNotDone = TRUE; |
int |
nRecord = 0; |
int |
nDebug = FALSE; |
int |
nNeedSaving = FALSE; |
double |
dSales = 0.0; /* Forces loading of floating-point support */ |
printf(“Please enter customer save file name: “);
gets(szFileName);
DataFile = fopen(szFileName, “wt”);
if (DataFile == NULL)
{/* Test for file open. If the file can’t be opened, exit with message. */
printf(“ERROR: File ‘%s’ couldn’t be opened.\n”, szFileName);
exit(4);
}
fclose(DataFile);
printf(“Demo of a linked list concepts\n” “\n”
“ Commands are:\n”
“ |
A |
- |
Add a customer/supplier record.\n” |
“ |
D |
- |
Display current list.\n” |
“ |
X |
- |
Exit from program.\n” |
continues
Part II • Managing Data in C
Listing 10.4. continued
“ |
Z |
- |
Toggle debug mode.\n” |
“ |
? |
- Display the command list.” |
“ |
H |
- Display the command list.” |
“ |
S |
- |
Save the list.\n” |
“\n” |
|
|
|
); |
|
|
|
nRecord = 0;
while (nNotDone)
{
printf(“Enter command (A, D+, D-, S)?”);
gets(szBuffer);
switch(szBuffer[0])
{
case ‘H’: /* Give some help */ case ‘h’:
case ‘?’:
GiveHelp();
break;
case ‘A’: /* Add a record */ case ‘a’:
Customer = (PCUSTNAME)calloc(sizeof(CUSTNAME),
INCREMENT_AMOUNT);
printf(“Enter name %d: “, ++nRecord); gets(szBuffer);
szBuffer[sizeof(Customer->szName) - 1] = ‘\0’; strcpy(Customer->szName, szBuffer);
if (strlen(Customer->szName) > 0)
{/* Insert this record in the list, sorted by name. */ nNeedSaving = TRUE;
Data Management: Sorts, Lists, and Indexes
if (FirstCustomer == NULL)
{
printf(“It is first record \n”); Customer->NextCustomer = NULL; Customer->PrevCustomer = NULL;
FirstCustomer = Customer;
LastCustomer = Customer; TempCustomer = NULL;
}
else
{
TempCustomer = FirstCustomer;
}
while (TempCustomer)
{
if (nDebug)
{
printf(“TESTING FOR ADD: ‘%s’ ‘%s’\n”, Customer->szName, TempCustomer->szName);
}
if (strcmp(Customer->szName, TempCustomer->szName) < 0 || TempCustomer == LastCustomer)
{
if (strcmp(Customer->szName, TempCustomer->szName) < 0 && TempCustomer == FirstCustomer)
{
if (nDebug)
{
printf(“Assigning as first\n”);
}
Customer->NextCustomer = FirstCustomer;
FirstCustomer = Customer;
Customer->PrevCustomer = NULL;
continues
Part II • Managing Data in C
Listing 10.4. continued
TempCustomer = Customer->NextCustomer; TempCustomer->PrevCustomer = Customer;
}
else
{
if (strcmp(Customer->szName, TempCustomer->szName) > 0 && TempCustomer == LastCustomer)
{
if (nDebug)
{
printf(“Assigning as last\n”);
}
Customer->PrevCustomer = LastCustomer; LastCustomer = Customer;
Customer->NextCustomer = NULL; TempCustomer = Customer- >PrevCustomer; TempCustomer->NextCustomer = Customer;
}
else
{
if (nDebug)
{
printf(“Assigning inside \ list\n”);
}
Customer->PrevCustomer =
TempCustomer->PrevCustomer;
Customer->NextCustomer =
TempCustomer;
TempCustomer->PrevCustomer =
Customer;
TempCustomer = Customer-
>PrevCustomer;
TempCustomer->NextCustomer =
Customer;
Data Management: Sorts, Lists, and Indexes |
C C C |
|
10C |
|
C C C |
} |
C C |
|
} |
|
TempCustomer = NULL; |
|
}
else
{
TempCustomer = TempCustomer->NextCustomer;
}
}
Customer->nRecordNumber = nRecord;
if (!nDebug)
{
do
{
printf(“Enter 1 for customer, 2 for supplier \ “);
gets(szBuffer);
sscanf(szBuffer, “%d”, &Customer ->nRecordType);
}
while (Customer->nRecordType != CUSTOMER_RECORD
&&
Customer->nRecordType != SUPPLIER_RECORD);
printf(“Enter address line 1: “); gets(szBuffer); szBuffer[sizeof(Customer->szAddr1) - 1] = ‘\0’; strcpy(Customer->szAddr1, szBuffer);
printf(“Enter address line 2: “); gets(szBuffer); szBuffer[sizeof(Customer->szAddr2) - 1] = ‘\0’; strcpy(Customer->szAddr2, szBuffer);
printf(“Enter City: “); gets(szBuffer);
szBuffer[sizeof(Customer->szCity) - 1] = ‘\0’; strcpy(Customer->szCity, szBuffer);
continues
Part II • Managing Data in C
Listing 10.4. continued
printf(“Enter state postal abbreviation: “); gets(szBuffer); szBuffer[sizeof(Customer->szState) - 1] = ‘\0’; strcpy(Customer->szState, szBuffer);
printf(“Enter ZIP code: “); gets(szBuffer);
sscanf(szBuffer, “%ld”, &Customer->lZip);
printf(“Enter total sales: “); gets(szBuffer);
sscanf(szBuffer, “%f”, &Customer->dSalesTotal);
}
}
else
{
printf(“\aSorry, name must not be blank!\n”);
}
break;
case ‘Z’: /* Debug mode toggle */ case ‘z’:
nDebug = !nDebug; break;
case ‘D’: /* Display all records */ case ‘d’:
TempCustomer = FirstCustomer;
printf(“Display customers\n”);
while (TempCustomer)
{
if (nDebug)
{
printf(
“Name ‘%10s’ Me %lp Next %lp Prev %lp\n”, TempCustomer->szName,
TempCustomer,