/*--- llgen.h-------------------------------------------------- * Declarations for generic double linked lists. * Used in conjucntion with llgen.c *-------------------------------------------------------------*/ #ifndef LLGEN_H /* make sure it's included only once */ #define LLGEN_H struct Node { struct Node *prev; /*link to previous node*/ struct Node *next; /*link to next node*/ void *pdata; /* generic pointer to data */ }; typedef struct Node* Link; /* a linked list data structure */ struct List { Link LHead; Link LTail; unsigned int LCount; void* (*LCreateData) (void*); int (*LDeleteData) (void*); int (*LDuplicatedData) (Link, Link); int (*LNodeDataCmp) (void*, void*); }; /* The four function specific to an individual linked list are: LCreateData: is passed a pointer to an application-defined object and is expected to return a pointer to whatever is to be stored in the linked list. LDeleteData: is passed a pointer to the object an application has stored in a linked list. LDeleteData must destroy the object LDuplicatedNode: is passed two pointers. The first pointer is to a node that you would like to add to a linked list and the second is to a node that is already in the list but is a duplicate of the first pointer. LDuplicateNode returns: 0->do nothing to list 1->destroy duplicate 2->add duplicate to list LNodeDataCmp: is passed pointers to two application data objects and must compare them, returning a number that is < 0, zero, or >0, depending on the relationship bewtween the first and second objects. */ /* ---------------- generic linked-list primitives ------------------*/ int AddNodeAscend( struct List *, void *); int AddNodeAtHead( struct List *, void *); struct List * CreateList( void *(*)(void*), /* create data */ int (*)(void*), /* delete data */ int (*)(Link, Link), /* duplicate */ int (*)(void *, void *)); /* compare */ Link CreateNode (struct List*, void *); int DeleteNode (struct List*, Link); Link FindNode (struct List*, void *); Link FindNodeAscend (struct List*, void *); Link GotoNext (struct List*, Link); Link GotoPrev (struct List*, Link); /** 阅读后注释: 本文件实现了一个通用双向链的接口声明,从中我们可以学到: 1. 在C语言中使用void*保存与应用相关的数据; 2. 通过函数指针实现通用的逻辑操作 3. 函数指针声明可以看到就是比一般函数声明在函数名左边增加一个星号 并用括号括住; 4. 对于C++实质上我们可以使用多态来声明一个通用双向链,这也就说明 使用C语言的函数指针和void*同样可以实质多态 **/ #endif /* llgen.c * Generic primitive functions for doubly linked lists. * Contains no application-specific functions. * Functions are in alphabetical order */ #include <stdlib.h> #include <stdio.h> #define IN_LL_LIB 1 /* in the library of primitives */ #include "llgen.h" /* Aliases to make the code more readable */ #define LLHead (L->LHead) /* The head of the current list */ #define LLTail (L->LTail) /* The tail of the current list */ #define NodeCount (L->LCount) /* Nodes in the current list */ #define CreateData (*(L->LCreateData)) #define DeleteData (*(L->LDeleteData)) #define DuplicatedData (*(L->LDuplicatedData)) #define NodeDataCmp (*(L->LNodeDataCmp)) /*----------------------------------------------------------- * Add a node at head: first allocated the space for * the data, then allocate a node with a pointer to *-----------------------------------------------------------*/ int AddNodeAtHead(struct List *L, void *nd) { Link pn; pn = CreateNode(L, nd); if (pn==NULL) return (0); /*--- Add the node ---*/ if (LLHead == NULL) /* is it the first node? */ { LLHead = LLTail = pn; /*--- yes ---*/ }else { LLHead->prev = pn; /* first goes node before Head */ pn->next = LLHead; /* put Head next */ LLHead = pn; /* then point Head to us */ } NodeCount += 1; return 1; } /************************************************************************/ /* Add ascending. Adds a node to an ordered list. */ /************************************************************************/ int AddNodeAscend(struct List *L, void *nd) { Link pn; /* to node we're creating */ Link prev, curr; /* our current search */ struct Node dummy; /* a dummy node */ int compare; pn = CreateNode(L, nd); if (pn==NULL) return 0; /* attach dummy node to head of list */ dummy.next = LLHead; dummy.prev = NULL; if (dummy.next != NULL) dummy.next->prev = &dummy; prev = &dummy; curr = dummy.next; for (;curr!=NULL; prev=curr, curr=curr->next) { compare = NodeDataCmp(pn->pdata, curr->pdata); if (compare<0) break; /* new node equals or precedes curr */ } if (curr!=NULL && compare==0) { compare = DuplicatedData(pn,curr); if (compare==2) /* do nothing -- will get inserted */; else{ /* first , repair the linked list */ LLHead = dummy.next; LLHead->prev = NULL; /* delete the dulicated node , if appropriate */ if (compare==1) { DeleteData(pn->pdata); free(pn); } return 1; } } prev->next = pn; pn->prev = prev; pn->next = curr; if (curr!=NULL) curr->prev = pn; else LLTail = pn; /* this node is the new tail */ NodeCount += 1; /* now , unhook the dummy head node */ LLHead = dummy.next; LLHead->prev = NULL; return 1; } /*-------------------------------------------------------------- * Create a linked-list structure and returns a pointer to it. * On error, returns NULL. This functions accepts pointers * to the four list-specific functions and initialized the * linked-list structure with them. *--------------------------------------------------------------*/ struct List * CreateList( void* (*fCreateData)(void *), int (*fDeleteData)(void *), int (*fDuplicatedNode)(Link, Link), int (*fNodeDataCmp)(void*, void*)) { struct List* pL; pL=(struct List*)malloc(sizeof(struct List)); if(pL==NULL) return NULL; pL->LHead = NULL; pL->LTail = NULL; pL->LCount = 0; pL->LCreateData = fCreateData; pL->LDeleteData = fDeleteData; pL->LDuplicatedData = fDuplicatedNode; pL->LNodeDataCmp = fNodeDataCmp; return pL; } /* * Create a node and then calls the application-specific * function CreateData() to create the node's data structure. * Returns NULL on error. */ Link CreateNode(struct List* L, void *data) { Link new_node; new_node = (Link)malloc(sizeof(struct Node)); if (new_node == NULL) return (NULL); new_node->prev = NULL; new_node->next = NULL; /* now call the application-specific data allocation */ new_node->pdata = CreateData(data); if (new_node->pdata == NULL) { free(new_node); return NULL; }else return new_node; } /* * Deletes the node pointed to by to_delete. * Function calls list-specific function to delete data. */ int DeleteNode(struct List* L, Link to_delete) { Link pn; if ( to_delete == NULL ) /* Double check before */ return 0; if (to_delete->prev==NULL) /* we're at the head */ { LLHead = to_delete->next; /* update head */ LLHead->prev = NULL; /* update next node */ } else if (to_delete->next==NULL) { /* we're at the tail */ pn = to_delete->prev; /* get the previous node */ pn->next = NULL; LLTail = pn; /* update tail */ } else { pn = to_delete->prev; /* get the previous node */ pn->next = to_delete->next; /* point to the next one */ pn = to_delete->next; /* get the next node */ pn->prev = to_delete->prev; /* update it to point to the previouse node */ } DeleteData(to_delete->pdata); /* delete the data */ free( to_delete ); /* free the node */ NodeCount -= 1; return 1; } /* * Finds node by starting at the head of the list, stepping * through each node, and comparing data items with the search * key. The Ascend version checks that the data in the node * being examined is not larger than the search key. If it is, * we know the key is not in the list. Returns pointer to node * on success or NULL on failure. */ Link FindNode(struct List*L, void *nd) { Link pcurr; /* the node we're examining */ if (LLHead == NULL) /* empty list */ return NULL; for (pcurr=LLHead; pcurr!=NULL; pcurr=pcurr->next) { if (NodeDataCmp(nd,pcurr->pdata)==0) return pcurr; } return NULL; /* could not find node */ } Link FindNodeAscend(struct List* L, void *nd) { Link pcurr; /* the node we're examining */ int cmp_result; if (LLHead==NULL) /* empty list */ return NULL; for (pcurr=LLHead; pcurr!=NULL; pcurr=pcurr->next) { cmp_result = NodeDataCmp(nd, pcurr->pdata); if (cmp_result<0) return NULL; /* too far */ else if (cmp_result==0) /* just right */ return pcurr; else ; } return NULL; /* could not find node */ } /* * 阅读后注释: * 本文件是通用双向链接的实现文件,从代码中我们可以学习到: * 1. 所有函数都采用宏,即通过调用相应链接的函数指针完成,这类似于 * C++的类成员函数调用; * 2. 在创建结果的地方我们可以看到,这里没有直接使用memcpy或memmove * 这是因为这里必须是深拷贝,否则就会使用自定义数据只是简单地增加 * 指针引用而不是重新分配空间至新结点中。这个也就是所说的“数据 * 耐力”问题 * 3. 函数指针要引用它来调用函数使用* */ /* * llapp.h * Application-specific data for linked list in lldriver.c * used in conjunction with llapp.c */ #ifndef LLAPP_H #define LLAPP_H 1 /* * our first list's nodes consist of a pointer to * a word and a count of occurrence. */ struct NodeData1 { char *word; unsigned int u; }; typedef struct NodeData1 *pND1; extern void * CreateData1( void * ); extern int DeleteData1( void * ); extern int DuplicateNode1( Link, Link ); extern int NodeDataCmp1( void *, void* ); /* * Our second list's nodes consist of a * pointer to a word. */ struct NodeData2{ char *word; }; typedef struct NodeData2* pND2; extern void *CreateData2( void * ); extern int DeleteData2( void * ); extern int DuplicateNode2(Link, Link); extern int NodeDataCmp2(void *, void *); /* * 阅读后注释: * 如果看到这段代码你就会想到,C++的多态是多么美好的东西! */ #endif /* * llapp.c * Application-specific functions for linked-list examples. * Replace these routines with your own. */ #include <stdlib.h> /* for free() */ #include <string.h> /* for strcmp() and strdup() */ #include "llgen.h" #include "llapp.h" /* data is a pointer to a string */ void * CreateData1( void * data ) { struct NodeData1* new_data; /* allocate our data structure */ new_data = malloc(sizeof(struct NodeData1)); if (new_data==NULL) return NULL; /* move the values into the data structure */ new_data->u = 1; new_data->word = strdup( (char *) data ); if ( new_data->word == NULL) /* error copying string */ { free(new_data); return NULL; } else return new_data; /* return a complete structure */ } int DeleteData1( void * data ) { /* * In this case, NodeData1 consists of: a pointer and an int. * The integer will be returned to memory when the node * is freed. However, the string must be freed manually. */ free( ((pND1)data)->word ); return 1; } /* * This function determines what to do when inserting a node * into a list if an existing node with the same data is found * in the list. In this case, since we are counting words, if a * duplicate word is found, we simply increment the counter. * * Note this function should return one of the following values: * 0 an error occurred * 1 delete the duplicate node * 2 insert the duplicate node * Any other processing on the duplicate should be done in this * function. */ int DuplicateNode1( Link new_node, Link list_node) { /* adding an occurrence to an existing word */ pND1 pnd = list_node->pdata; pnd->u += 1; return 1; } int NodeDataCmp1( void * first, void* second) { return strcmp( ((pND1)first)->word, ((pND1)second)->word); } /* Now the functions for the second linked list */ void * CreateData2( void * data ) { struct NodeData2 * new_data; new_data = malloc(sizeof(struct NodeData2)); /* allocate the data structure */ if (new_data == NULL) return NULL; /* move the values into data structure */ new_data->word = strdup((char*)data); if (new_data == NULL) /* error copying string */ { free( new_data ); return NULL; } else return new_data; } int DeleteData2(void * data) { /* * In this case , NodeData2 consists of a pointer. * The string must be freed manually. */ free( ((pND2)data)->word); return 1; } /* this list inserts duplicated nodes */ int DuplicateNode2(Link new_node, Link list_node) { return 2; } int NodeDataCmp2(void *first, void *second) { return (strcmp(((pND2)first)->word,((pND2)second)->word )); } /* * lldriver.c * Reads in text words from the file specified on the command * line and places them into two linked lists. Then exercises * a variety of linked-list activities, printing the results * at every step. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "llgen.h" /* Header for generic linked lists */ #include "llapp.h" /* Header for appl.'s linked lists */ int main(int argc, char *argv[]) { char word[64]; /* the raw word from the file */ int count; struct List *L1, *L2; /* two different linked lists */ Link w1, w2, w3; /* cursors used to walk lists */ FILE *fin; /* the input file */ if (argc!=2) { fprintf( stderr, "Error: Usages: lldriver filename/n" ); exit( EXIT_FAILURE ); } fin = fopen( argv[1], "rt" ); if (fin == NULL) { fprintf( stderr, "Could not find/open %s/n", argv[1]); exit( EXIT_FAILURE ); } /* set up linkd-list data structures */ L1 = CreateList( CreateData1, DeleteData1, DuplicateNode1, NodeDataCmp1); L2 = CreateList( CreateData2, DeleteData2, DuplicateNode2, NodeDataCmp2); if ( L1==NULL || L2==NULL ) { fprintf(stderr, "Error creating linked list/n"); exit(EXIT_FAILURE); } /* begin processing file */ while ( fgets(word, 64, fin)!=NULL ) { if (strlen(word)>0) word[strlen(word)-1] = 0; /* strip tail /n */ /* now add the word to both lists */ if ( !AddNodeAscend(L1, word)) fprintf(stderr, "Waring!Error while adding node to L1./n"); if ( !AddNodeAtHead(L2, word)) fprintf(stderr, "Waring!Error while adding node to L2./n"); } fclose(fin); /* now, walk the lists */ printf("L1 contains %u item:/n", L1->LCount); for (w1=L1->LHead; w1!=NULL; w1=w1->next) printf(" %s occured %d times./n", ((pND1)(w1->pdata))->word, ((pND1)(w1->pdata))->u); printf("L2 contains %u item:/n", L2->LCount); for (w1=L2->LHead; w1!=NULL; w1=w1->next) printf(" %s/n", ((pND2)(w1->pdata))->word); /* both ways at once */ printf("L2 contains %u items:/n", L2->LCount); w1=L2->LHead; w2=L2->LTail; for (;w1!=NULL&&w2!=NULL;w1=w1->next, w2=w2->prev) printf(" %30s %30s/n", ((pND2)(w1->pdata))->word, ((pND2)(w2->pdata))->word); /* "Find" each node and delete every other one */ count=0; w1=L2->LHead; while(w1!=NULL) { w3=FindNode(L2, w1->pdata); if (w3!=0) { printf("Found node %s", ((pND2)(w3->pdata))->word); count+=1; w1=w3->next; if (count&1) { DeleteNode(L2, w3); printf(" and deleted it."); } printf("/n"); } else w1= NULL; } printf("L2 contains %u items:/n", L2->LCount); w1=L2->LHead; w2=L2->LTail; for (;w1!=NULL&&w2!=NULL;w1=w1->next, w2=w2->prev) printf(" %30s %30s/n", ((pND2)(w1->pdata))->word, ((pND2)(w2->pdata))->word); return (EXIT_SUCCESS); }