使用尾指针插入链表的末尾,然后打印列表导致无限循环
Inserting at the end of a linked list using a tail pointer, then printing the list cause infinite loop
我正在创建一个程序:
1) 读取一些以 "STOP" 结尾的词
2)使用尾指针将它们中的每一个插入链表的末尾
3)然后打印每个单词
当我尝试打印列表时,输出是第一个插入单词的无限循环。
//max len of a word in input
#define WORD 20
//define a node for a linked list
typedef struct node_l{
char string[WORD+1];
struct node_l *next;
}Node_L;
Node_L *newNode_L(char *word);
char *readword(void);
void printList(Node_L *list);
void insertNode_L(Node_L **list, Node_L **tail, char *word);
int main(){
Node_L *root = NULL;
Node_L *tail = NULL;
char *word = readword();
while(strcmp(word,"STOP")!=0){
insertNode_L(&root,&tail, word);
word = readword();
}
printList(root);
return 0;
}
/*given head and tail pointer and a word:
-creates a node containing the word
-insert at the end of the linked list
*/
void insertNode_L(Node_L **list, Node_L **tail, char *word){
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
}else{
Node_L *t = *tail;
t->next = *tail;
*tail = temp;
}
}
//given a word returns a new node(linked list type) containing that word
Node_L *newNode_L(char *word){
Node_L *node = malloc(sizeof(Node_L));
if(node==NULL){
printf("malloc failure\n");
exit(EXIT_FAILURE);
}
strcpy(node->string, word);
node->next = NULL;
return node;
}
/*function that:
-first skips non alphabetical characters
-reads every char until a not alphabetical character is inserted
-returns a pointer to the first char of the new created string
*/
char *readword(void){
int startWlen = 2;
char *word= malloc(startWlen);
//skips whitespace and char that are not alphabetical
int c = getchar();
while(!isalpha(c)){
c = getchar();
}
int i=0;
while(isalpha(c)){
//realloc if necessary
if(i==startWlen){
startWlen*=2;
char *temp = realloc(word, startWlen);
if(temp==NULL){
printf("realloc failure\n");
exit(EXIT_FAILURE);
}
word = temp;
}
word[i]=c;
i++;
c = getchar();
}
//realloc if necessary for '[=10=]' char
if(i==startWlen){
startWlen+=1;
char *p =realloc(word,startWlen+1);
if (p == NULL){
printf("realloc failure\n");
exit(EXIT_FAILURE);
}else{
word = p;
}
}
word[i]='[=10=]';
return word;
}
//TRAVERSING A LINKED LIST
void printList(Node_L *list){
int i=0;
Node_L *p = list;
while(p!=NULL){
printf("%s is in pos %d \n", p->string,i);
p = p->next;
i++;
if(i ==10) break; //to limit infinite loop
}
}
示例:
INPUT:train rocket ball STOP
OUTPUT:
train
train
train
train
train
train
train
train
train
train
提前致谢
注意就像答案中解释的一样,问题是插入函数;
现在我 post 这里是它的更正版本:
void insertNode_L(Node_L **list, Node_L **tail, char *word){
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
}else{
(*tail)->next = temp;
*tail = temp;
}
}
使您的列表双向链接。这是我 1999 年的实现:
/*
* List.c - doubly linked list library
*
* DESCRIPTION
* This module contains routines to create and maintain doubly linked
* lists of data objects. A list can be used for storing data object
* pointers or integer values (except zero).
*
* The application using this library only has to deal with a single list
* pointer and its data objects. It does not have to deal with list nodes
* as is often seen, neither do the the data objects need to supply space
* for list pointers (often called <pNext> and <pPrev>). This list type
* is generally called to be 'non-intrusive'.
* The price paid for this convenience is that nodes can not be accessed
* randomly, which means that deleting a node may require a linear search.
* The list does however keeps a pointer to the last accessed list node;
* the supplied set of operations relative to this node still makes this
* kind of list very useful for many applications without (much) perfor-
* mance loss compared to 'more traditional' linked lists in C.
*
* NOTES
* Doing something that is not allowed, or entering a condition that is
* regarded as an error, will result in a 'failed assertion', when this
* module has been built with DEBUG defined. The routine descriptions
* tell what to watch out for.
*
* INTERNAL
* The idea of using a dummy node was taken from "Obfuscated C and Other
* Mysteries" by Don Libes, John Wiley & Sons - 1993, chapter 11. It
* results in simpler list operation code.
*
* down--> <--up
* after--> <--before
*
* +-------------------- - --------------+
* | |
* +--------------+ V Head Node Tail Node |
* | | +-------+ +-------+ +-------+ |
* | pHead---------->|/pNext------>| pNext---- - -->| pNext----+
* | | |///////| | | | |
* | pNodeLast--->? +----pPrev/|<------pPrev |<- - -----pPrev |
* | | | |///////| | | | |
* | count | | |/pData/| | pData | +->| pData |
* | | | +-- | --+ +-- | --+ | +-- | --+
* +--------------+ | | | | |
* | V V | V
* | ### ### | ###
* //// = Dummy Node | ### ### | ###
* | |
* ### = User Data +--------------------------- - +
*
* Notice that pList->pHead->pPrev points to the tail of the list; this
* is used a number of times in the code below.
*
* For efficiency some short code fragments show up a number of times
* in different routines, instead of nesting the routines.
*
* INCLUDE FILES
* List.h
*
* COPYRIGHT
* You are free to use, copy or modify this software at your own risk.
*
* AUTHOR
* meaning-matters
*
* MODIFICATION HISTORY
* 1999/04/12 MM Thorough test and debugging; beta release.
* 1999/03/09 MM Composed.
*
*****************************************************************************/
#include <stdlib.h>
#include "List.h"
#include "Assert.h" /* includes "Except.h" which defines return() macro */
/******************************************************************************
*
* ListCreate - create empty list
*
* DESCRIPTION
* This routine creates an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to empty list.
*/
List * ListCreate(void)
{
List * pList;
pList = malloc(sizeof(List));
pList->pHead = malloc(sizeof(ListNode));
pList->pHead->pNext = pList->pHead->pPrev = pList->pHead;
pList->pNodeLast = NULL;
pList->count = 0;
return pList;
}
/******************************************************************************
*
* ListDestroy - free list but not user data
*
* DESCRIPTION
* This routine frees the list handle and the nodes, but does not free
* the user data.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListDestroy(
List * pList) /* pointer to list */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead)
{
ListNode * pNext;
pNext = pNode->pNext;
free(pNode);
pNode = pNext;
}
free(pList->pHead);
free(pList);
}
/******************************************************************************
*
* ListDestroyData - free list including user data
*
* DESCRIPTION
* This routine frees the list handle and the nodes, and does also free
* the user data using free(); the caller is responsible that all of this
* user data was allocated with routines compatible with free().
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListDestroyData(
List * pList) /* pointer to list */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead)
{
ListNode * pNext;
pNext = pNode->pNext;
free(pNode->pData);
free(pNode);
pNode = pNext;
}
free(pList->pHead);
free(pList);
}
/******************************************************************************
*
* ListAddHead - add node to head of list
*
* DESCRIPTION
* This routine adds the specified data object value at the head of the
* specified list. The last accessed list node is set to the added
* node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddHead(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pNext = pList->pHead->pNext)->pPrev = pNode;
(pList->pHead->pNext = pNode)->pPrev = pList->pHead;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddTail - add node to tail of list
*
* DESCRIPTION
* This routine adds the specified data object value at the tail of the
* specified list. The last accessed list node is set to the added
* node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddTail(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pPrev = pList->pHead->pPrev)->pNext = pNode;
(pList->pHead->pPrev = pNode)->pNext = pList->pHead;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddBefore - add node before last accessed node
*
* DESCRIPTION
* This routine adds the specified data object value in the specified
* list just before the node that was last accessed by one of the
* routines from this library that set it. 'Before' means towards the
* head of the list. The last accessed list node is set to the added
* node.
*
* Nothing happens when the last accessed list node is not set; this
* causes a failed assertion when DEBUG defined.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddBefore(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NOTHING);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pPrev = pList->pNodeLast->pPrev)->pNext = pNode;
(pList->pNodeLast->pPrev = pNode)->pNext = pList->pNodeLast;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddAfter - add node after last accessed node
*
* DESCRIPTION
* This routine adds the specified data object value in the specified
* list right after the node that was last accessed by one of the
* routines from this library that set it. 'After' means towards the
* tail of the list. The last accessed list node is set to the added
* node.
*
* Nothing happens when the last accessed list node is not set; this
* causes a failed assertion when DEBUG defined.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddAfter(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NOTHING);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pNext = pList->pNodeLast->pNext)->pPrev = pNode;
(pList->pNodeLast->pNext = pNode)->pPrev = pList->pNodeLast;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListRemoveHead - remove head node from list
*
* DESCRIPTION
* This routine removes the head list node from the specified list. The
* last accessed list node is reset.
*
* It is not allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveHead(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pNext;
pData = pNode->pData;
(pList->pHead->pNext = pNode->pNext)->pPrev = pList->pHead;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemoveTail - remove tail node from list
*
* DESCRIPTION
* This routine removes the tail list node from the specified list. The
* last accessed list node is reset.
*
* It is not allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveTail(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pPrev;
pData = pNode->pData;
(pList->pHead->pPrev = pNode->pPrev)->pNext = pList->pHead;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemove - remove specified node from list
*
* DESCRIPTION
* This routine removes the node with the specified data object value
* The last accessed list node is reset.
*
* It is an error if the specified node is not in the list. It is not
* allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemove(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead && pNode->pData != pData)
pNode = pNode->pNext;
validate(pNode->pData == pData, NULL);
pNode->pNext->pPrev = pNode->pPrev;
pNode->pPrev->pNext = pNode->pNext;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemoveLast - remove last accessed node from list
*
* DESCRIPTION
* This routine removes the node which was last accessed by one of the
* routines in this library that set it. Subsequently the last accessed
* list node is set to the next node for convenience.
*
* It is an error if the last accessed node was not set by one of the
* routines in this library. It is not allowed to pass this routine an
* empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveLast(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNext;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
pData = pList->pNodeLast->pData;
pNext = pList->pNodeLast->pNext;
pList->pNodeLast->pNext->pPrev = pList->pNodeLast->pPrev;
pList->pNodeLast->pPrev->pNext = pList->pNodeLast->pNext;
free(pList->pNodeLast);
pList->pNodeLast = pNext;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListHead - get head data object value
*
* DESCRIPTION
* This routine returns the user data object value of the head node of
* the specified list. The last accessed list node is reset if the list
* is empty, otherwise it is set to the list head.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Head data object value, or NULL if empty.
*/
void * ListHead(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->count == 0)
return NULL;
else
return 0, (pList->pNodeLast = pList->pHead->pNext)->pData;
}
/******************************************************************************
*
* ListTail - get tail data object value
*
* DESCRIPTION
* This routine returns the user data object value of the tail node of
* the specified list. The last accessed list node is reset if the list
* is empty, otherwise it is set to the list tail.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Head data object value, or NULL if empty.
*/
void * ListTail(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->count == 0)
return NULL;
else
return 0, (pList->pNodeLast = pList->pHead->pPrev)->pData;
}
/******************************************************************************
*
* ListLast - get last accessed data object value
*
* DESCRIPTION
* This routine returns the user data object value of the last accessed
* node of the specified list. The last accessed list node is not
* affected.
*
* When the last accessed list node is not set, which is also the case
* when the list is empty, NULL is returned.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Last accessed data object value, or NULL if not set.
*/
void * ListLast(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->pNodeLast == NULL)
return NULL;
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListNext - get next data object value
*
* DESCRIPTION
* This routine returns the user data object value of the next node
* with respect to the last accessed list node. The last accessed list
* node is set to the next node, or is reset if the tail is passed.
*
* It is an error if the last accessed list node is not set; which is
* also the case when the list is empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Next data object value, or NULL if already at tail.
*/
void * ListNext(
List * pList) /* pointer to list */
{
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
if ((pList->pNodeLast = pList->pNodeLast->pNext) == pList->pHead)
{
pList->pNodeLast = NULL;
return NULL;
}
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListPrev - get previous data object value
*
* DESCRIPTION
* This routine returns the user data object value of the previous node
* with respect to the last accessed list node. The last accessed list
* node is set to the previous node, or is reset if the head is passed.
*
* It is an error if the last accessed list node is not set; which is
* also the case when the list is empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Next data object value, or NULL if already at head.
*/
void * ListPrev(
List * pList) /* pointer to list */
{
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
if ((pList->pNodeLast = pList->pNodeLast->pPrev) == pList->pHead)
{
pList->pNodeLast = NULL;
return NULL;
}
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListCount - report number of nodes in list
*
* DESCRIPTION
* This routine returns the number of nodes in the specified list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Number of nodes in list.
*/
int ListCount(
List * pList) /* pointer to list */
{
assert(pList != NULL);
return pList->count;
}
/******************************************************************************
*
* ListFind - find list node
*
* DESCRIPTION
* This routine finds the node with the specified data object value. If
* nothing was found the last accessed list node is not affected,
* otherwise it is set to the found node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Found data object value, or NULL if not found or empty list.
*/
void * ListFind(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead && pNode->pData != pData)
pNode = pNode->pNext;
if (pNode->pData == pData)
{
pList->pNodeLast = pNode;
return pData;
}
else
return NULL;
}
/******************************************************************************
*
* ListSplitBefore - split list just before last node
*
* DESCRIPTION
* This routine splits up the specified list in two parts. The split is
* made just before the last accessed list node. A new list is created
* for the part before the last accessed node. The last accessed list
* node for the original list is not affected. And the last accessed
* list node for the new list is reset.
*
* It is not allowed to pass this routine an empty list. If the list
* contains only one node, the new list will be empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to new list containing nodes before original last accessed
* list node.
*/
List * ListSplitBefore(
List * pListOrg) /* pointer to original list */
{
List * pListNew;
ListNode * pNodeOrg;
assert(pListOrg != NULL);
validate(pListOrg->count > 0, NULL);
validate(pListOrg->pNodeLast != NULL, NULL);
pListNew = malloc(sizeof(List));
pListNew->pHead = malloc(sizeof(ListNode));
pListNew->pHead->pData = NULL;
pListNew->count = 0;
pNodeOrg = pListOrg->pHead->pNext;
while (pNodeOrg != pListOrg->pNodeLast)
{
pListOrg->count--;
pListNew->count++;
pNodeOrg = pNodeOrg->pNext;
}
if (pListNew->count == 0)
{
pListNew->pHead->pPrev = pListNew->pHead->pNext = pListNew->pHead;
}
else
{
/* connect list part to new list */
pListNew->pHead->pNext = pListOrg->pHead->pNext;
pListNew->pHead->pNext->pPrev = pListNew->pHead;
pListNew->pHead->pPrev = pListOrg->pNodeLast->pPrev;
pListNew->pHead->pPrev->pNext = pListNew->pHead;
/* bind last accessed node and original dummy node together */
pListOrg->pHead->pNext = pListOrg->pNodeLast;
pListOrg->pNodeLast->pPrev = pListOrg->pHead;
}
pListNew->pNodeLast = NULL;
return pListNew;
}
/******************************************************************************
*
* ListSplitAfter - split list just after last node
*
* DESCRIPTION
* This routine splits up the specified list in two parts. The split is
* made just after the last accessed list node. A new list is created
* for the part after the last accessed node. The last accessed list
* node for the original list is not affected. And the last accessed
* list node for the new list is reset.
*
* It is not allowed to pass this routine an empty list. If the list
* contains only one node, the new list will be empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to new list containing nodes after original last accessed
* list node.
*/
List * ListSplitAfter(
List * pListOrg) /* pointer to original list */
{
List * pListNew;
ListNode * pNodeOrg;
assert(pListOrg != NULL);
validate(pListOrg->count > 0, NULL);
validate(pListOrg->pNodeLast != NULL, NULL);
pListNew = malloc(sizeof(List));
pListNew->pHead = malloc(sizeof(ListNode));
pListNew->pHead->pData = NULL;
pListNew->count = 0;
pNodeOrg = pListOrg->pNodeLast->pNext;
while (pNodeOrg != pListOrg->pHead)
{
pListOrg->count--;
pListNew->count++;
pNodeOrg = pNodeOrg->pNext;
}
if (pListNew->count == 0)
{
pListNew->pHead->pPrev = pListNew->pHead->pNext = pListNew->pHead;
}
else
{
/* connect list part to new list */
pListNew->pHead->pNext = pListOrg->pNodeLast->pNext;
pListNew->pHead->pNext->pPrev = pListNew->pHead;
pListNew->pHead->pPrev = pListOrg->pHead->pPrev;
pListNew->pHead->pPrev->pNext = pListNew->pHead;
/* bind last accessed node and original dummy node together */
pListOrg->pNodeLast->pNext = pListOrg->pHead;
pListOrg->pHead->pPrev = pListOrg->pNodeLast;
}
pListNew->pNodeLast = NULL;
return pListNew;
}
/******************************************************************************
*
* ListConcat - concatenate two lists
*
* DESCRIPTION
* This routine concatenates the second list <pListAdd> to the tail of
* the first list <pListDst>. Either list (or both) may be empty. After
* the operation the <pListAdd> handle is destroyed. The last accessed
* list node of the resulting list is reset.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* List pointer <pListDst> containing the nodes of both lists.
*/
List * ListConcat(
List * pListDst, /* pointer to destination list */
List * pListAdd) /* pointer to list to be added at tail */
{
assert(pListDst != NULL);
assert(pListAdd != NULL);
switch (((pListAdd->count > 0) << 1) | (pListDst->count > 0))
{
case 0:
/* both lists empty */
break;
case 1:
/* destination list not empty and add list empty */
break;
case 2:
/* destination list empty and add list not empty */
pListDst->pHead->pNext = pListAdd->pHead->pNext;
pListDst->pHead->pNext->pPrev = pListDst->pHead;
pListDst->pHead->pPrev = pListAdd->pHead->pPrev;
pListDst->pHead->pPrev->pNext = pListDst->pHead;
break;
case 3:
/* both lists not empty */
pListAdd->pHead->pPrev->pNext = pListDst->pHead;
pListDst->pHead->pPrev->pNext = pListAdd->pHead->pNext;
pListAdd->pHead->pNext->pPrev = pListDst->pHead->pPrev;
pListDst->pHead->pPrev = pListAdd->pHead->pPrev;
break;
}
pListDst->pNodeLast = NULL;
pListDst->count += pListAdd->count;
free(pListAdd->pHead);
free(pListAdd);
return pListDst;
}
/* end of List.c */
以下是 header 的基本部分:
#ifndef _LIST_H
#define _LIST_H
typedef struct _ListNode ListNode;
struct _ListNode
{
ListNode * pNext; /* next node ('down', 'after') */
ListNode * pPrev; /* next node ('up', 'before') */
void * pData; /* pointer to user data */
};
typedef struct _List List; /* doubly linked list */
struct _List
{
ListNode * pHead; /* pointer to dummy list head node */
ListNode * pNodeLast; /* pointer to last accessed node */
int count; /* number of user nodes in list */
};
我知道这是一个有点 non-standard 的答案。但我认为看到另一个实现会有所帮助。
您的模型表明 *tail 为 NULL,并且是指向将来添加的节点(不是列表中的最后一项)的指针。完成插入后,您必须 re-assign 尾巴。
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
tail = temp->next;
}
问题出在您的 insertNode_L
(...) 方法的 else
块中。应该是
(*tail)->next = temp;
*tail = temp;
在你的代码中
Node_L *t = *tail;
t->next = *tail; // This makes tail point to itself resulting in an infinite loop
*tail = temp;
我正在创建一个程序: 1) 读取一些以 "STOP" 结尾的词 2)使用尾指针将它们中的每一个插入链表的末尾 3)然后打印每个单词
当我尝试打印列表时,输出是第一个插入单词的无限循环。
//max len of a word in input
#define WORD 20
//define a node for a linked list
typedef struct node_l{
char string[WORD+1];
struct node_l *next;
}Node_L;
Node_L *newNode_L(char *word);
char *readword(void);
void printList(Node_L *list);
void insertNode_L(Node_L **list, Node_L **tail, char *word);
int main(){
Node_L *root = NULL;
Node_L *tail = NULL;
char *word = readword();
while(strcmp(word,"STOP")!=0){
insertNode_L(&root,&tail, word);
word = readword();
}
printList(root);
return 0;
}
/*given head and tail pointer and a word:
-creates a node containing the word
-insert at the end of the linked list
*/
void insertNode_L(Node_L **list, Node_L **tail, char *word){
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
}else{
Node_L *t = *tail;
t->next = *tail;
*tail = temp;
}
}
//given a word returns a new node(linked list type) containing that word
Node_L *newNode_L(char *word){
Node_L *node = malloc(sizeof(Node_L));
if(node==NULL){
printf("malloc failure\n");
exit(EXIT_FAILURE);
}
strcpy(node->string, word);
node->next = NULL;
return node;
}
/*function that:
-first skips non alphabetical characters
-reads every char until a not alphabetical character is inserted
-returns a pointer to the first char of the new created string
*/
char *readword(void){
int startWlen = 2;
char *word= malloc(startWlen);
//skips whitespace and char that are not alphabetical
int c = getchar();
while(!isalpha(c)){
c = getchar();
}
int i=0;
while(isalpha(c)){
//realloc if necessary
if(i==startWlen){
startWlen*=2;
char *temp = realloc(word, startWlen);
if(temp==NULL){
printf("realloc failure\n");
exit(EXIT_FAILURE);
}
word = temp;
}
word[i]=c;
i++;
c = getchar();
}
//realloc if necessary for '[=10=]' char
if(i==startWlen){
startWlen+=1;
char *p =realloc(word,startWlen+1);
if (p == NULL){
printf("realloc failure\n");
exit(EXIT_FAILURE);
}else{
word = p;
}
}
word[i]='[=10=]';
return word;
}
//TRAVERSING A LINKED LIST
void printList(Node_L *list){
int i=0;
Node_L *p = list;
while(p!=NULL){
printf("%s is in pos %d \n", p->string,i);
p = p->next;
i++;
if(i ==10) break; //to limit infinite loop
}
}
示例:
INPUT:train rocket ball STOP
OUTPUT:
train
train
train
train
train
train
train
train
train
train
提前致谢
注意就像答案中解释的一样,问题是插入函数; 现在我 post 这里是它的更正版本:
void insertNode_L(Node_L **list, Node_L **tail, char *word){
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
}else{
(*tail)->next = temp;
*tail = temp;
}
}
使您的列表双向链接。这是我 1999 年的实现:
/*
* List.c - doubly linked list library
*
* DESCRIPTION
* This module contains routines to create and maintain doubly linked
* lists of data objects. A list can be used for storing data object
* pointers or integer values (except zero).
*
* The application using this library only has to deal with a single list
* pointer and its data objects. It does not have to deal with list nodes
* as is often seen, neither do the the data objects need to supply space
* for list pointers (often called <pNext> and <pPrev>). This list type
* is generally called to be 'non-intrusive'.
* The price paid for this convenience is that nodes can not be accessed
* randomly, which means that deleting a node may require a linear search.
* The list does however keeps a pointer to the last accessed list node;
* the supplied set of operations relative to this node still makes this
* kind of list very useful for many applications without (much) perfor-
* mance loss compared to 'more traditional' linked lists in C.
*
* NOTES
* Doing something that is not allowed, or entering a condition that is
* regarded as an error, will result in a 'failed assertion', when this
* module has been built with DEBUG defined. The routine descriptions
* tell what to watch out for.
*
* INTERNAL
* The idea of using a dummy node was taken from "Obfuscated C and Other
* Mysteries" by Don Libes, John Wiley & Sons - 1993, chapter 11. It
* results in simpler list operation code.
*
* down--> <--up
* after--> <--before
*
* +-------------------- - --------------+
* | |
* +--------------+ V Head Node Tail Node |
* | | +-------+ +-------+ +-------+ |
* | pHead---------->|/pNext------>| pNext---- - -->| pNext----+
* | | |///////| | | | |
* | pNodeLast--->? +----pPrev/|<------pPrev |<- - -----pPrev |
* | | | |///////| | | | |
* | count | | |/pData/| | pData | +->| pData |
* | | | +-- | --+ +-- | --+ | +-- | --+
* +--------------+ | | | | |
* | V V | V
* | ### ### | ###
* //// = Dummy Node | ### ### | ###
* | |
* ### = User Data +--------------------------- - +
*
* Notice that pList->pHead->pPrev points to the tail of the list; this
* is used a number of times in the code below.
*
* For efficiency some short code fragments show up a number of times
* in different routines, instead of nesting the routines.
*
* INCLUDE FILES
* List.h
*
* COPYRIGHT
* You are free to use, copy or modify this software at your own risk.
*
* AUTHOR
* meaning-matters
*
* MODIFICATION HISTORY
* 1999/04/12 MM Thorough test and debugging; beta release.
* 1999/03/09 MM Composed.
*
*****************************************************************************/
#include <stdlib.h>
#include "List.h"
#include "Assert.h" /* includes "Except.h" which defines return() macro */
/******************************************************************************
*
* ListCreate - create empty list
*
* DESCRIPTION
* This routine creates an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to empty list.
*/
List * ListCreate(void)
{
List * pList;
pList = malloc(sizeof(List));
pList->pHead = malloc(sizeof(ListNode));
pList->pHead->pNext = pList->pHead->pPrev = pList->pHead;
pList->pNodeLast = NULL;
pList->count = 0;
return pList;
}
/******************************************************************************
*
* ListDestroy - free list but not user data
*
* DESCRIPTION
* This routine frees the list handle and the nodes, but does not free
* the user data.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListDestroy(
List * pList) /* pointer to list */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead)
{
ListNode * pNext;
pNext = pNode->pNext;
free(pNode);
pNode = pNext;
}
free(pList->pHead);
free(pList);
}
/******************************************************************************
*
* ListDestroyData - free list including user data
*
* DESCRIPTION
* This routine frees the list handle and the nodes, and does also free
* the user data using free(); the caller is responsible that all of this
* user data was allocated with routines compatible with free().
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListDestroyData(
List * pList) /* pointer to list */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead)
{
ListNode * pNext;
pNext = pNode->pNext;
free(pNode->pData);
free(pNode);
pNode = pNext;
}
free(pList->pHead);
free(pList);
}
/******************************************************************************
*
* ListAddHead - add node to head of list
*
* DESCRIPTION
* This routine adds the specified data object value at the head of the
* specified list. The last accessed list node is set to the added
* node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddHead(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pNext = pList->pHead->pNext)->pPrev = pNode;
(pList->pHead->pNext = pNode)->pPrev = pList->pHead;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddTail - add node to tail of list
*
* DESCRIPTION
* This routine adds the specified data object value at the tail of the
* specified list. The last accessed list node is set to the added
* node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddTail(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pPrev = pList->pHead->pPrev)->pNext = pNode;
(pList->pHead->pPrev = pNode)->pNext = pList->pHead;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddBefore - add node before last accessed node
*
* DESCRIPTION
* This routine adds the specified data object value in the specified
* list just before the node that was last accessed by one of the
* routines from this library that set it. 'Before' means towards the
* head of the list. The last accessed list node is set to the added
* node.
*
* Nothing happens when the last accessed list node is not set; this
* causes a failed assertion when DEBUG defined.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddBefore(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NOTHING);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pPrev = pList->pNodeLast->pPrev)->pNext = pNode;
(pList->pNodeLast->pPrev = pNode)->pNext = pList->pNodeLast;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListAddAfter - add node after last accessed node
*
* DESCRIPTION
* This routine adds the specified data object value in the specified
* list right after the node that was last accessed by one of the
* routines from this library that set it. 'After' means towards the
* tail of the list. The last accessed list node is set to the added
* node.
*
* Nothing happens when the last accessed list node is not set; this
* causes a failed assertion when DEBUG defined.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* N/A.
*/
void ListAddAfter(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NOTHING);
pNode = malloc(sizeof(ListNode));
pNode->pData = pData;
(pNode->pNext = pList->pNodeLast->pNext)->pPrev = pNode;
(pList->pNodeLast->pNext = pNode)->pPrev = pList->pNodeLast;
pList->pNodeLast = pNode;
pList->count++;
}
/******************************************************************************
*
* ListRemoveHead - remove head node from list
*
* DESCRIPTION
* This routine removes the head list node from the specified list. The
* last accessed list node is reset.
*
* It is not allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveHead(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pNext;
pData = pNode->pData;
(pList->pHead->pNext = pNode->pNext)->pPrev = pList->pHead;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemoveTail - remove tail node from list
*
* DESCRIPTION
* This routine removes the tail list node from the specified list. The
* last accessed list node is reset.
*
* It is not allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveTail(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pPrev;
pData = pNode->pData;
(pList->pHead->pPrev = pNode->pPrev)->pNext = pList->pHead;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemove - remove specified node from list
*
* DESCRIPTION
* This routine removes the node with the specified data object value
* The last accessed list node is reset.
*
* It is an error if the specified node is not in the list. It is not
* allowed to pass this routine an empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemove(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
validate(pList->count > 0, NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead && pNode->pData != pData)
pNode = pNode->pNext;
validate(pNode->pData == pData, NULL);
pNode->pNext->pPrev = pNode->pPrev;
pNode->pPrev->pNext = pNode->pNext;
free(pNode);
pList->pNodeLast = NULL;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListRemoveLast - remove last accessed node from list
*
* DESCRIPTION
* This routine removes the node which was last accessed by one of the
* routines in this library that set it. Subsequently the last accessed
* list node is set to the next node for convenience.
*
* It is an error if the last accessed node was not set by one of the
* routines in this library. It is not allowed to pass this routine an
* empty list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Removed data object value.
*/
void * ListRemoveLast(
List * pList) /* pointer to list */
{
void * pData;
ListNode * pNext;
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
pData = pList->pNodeLast->pData;
pNext = pList->pNodeLast->pNext;
pList->pNodeLast->pNext->pPrev = pList->pNodeLast->pPrev;
pList->pNodeLast->pPrev->pNext = pList->pNodeLast->pNext;
free(pList->pNodeLast);
pList->pNodeLast = pNext;
pList->count--;
return pData;
}
/******************************************************************************
*
* ListHead - get head data object value
*
* DESCRIPTION
* This routine returns the user data object value of the head node of
* the specified list. The last accessed list node is reset if the list
* is empty, otherwise it is set to the list head.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Head data object value, or NULL if empty.
*/
void * ListHead(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->count == 0)
return NULL;
else
return 0, (pList->pNodeLast = pList->pHead->pNext)->pData;
}
/******************************************************************************
*
* ListTail - get tail data object value
*
* DESCRIPTION
* This routine returns the user data object value of the tail node of
* the specified list. The last accessed list node is reset if the list
* is empty, otherwise it is set to the list tail.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Head data object value, or NULL if empty.
*/
void * ListTail(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->count == 0)
return NULL;
else
return 0, (pList->pNodeLast = pList->pHead->pPrev)->pData;
}
/******************************************************************************
*
* ListLast - get last accessed data object value
*
* DESCRIPTION
* This routine returns the user data object value of the last accessed
* node of the specified list. The last accessed list node is not
* affected.
*
* When the last accessed list node is not set, which is also the case
* when the list is empty, NULL is returned.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Last accessed data object value, or NULL if not set.
*/
void * ListLast(
List * pList) /* pointer to list */
{
assert(pList != NULL);
if (pList->pNodeLast == NULL)
return NULL;
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListNext - get next data object value
*
* DESCRIPTION
* This routine returns the user data object value of the next node
* with respect to the last accessed list node. The last accessed list
* node is set to the next node, or is reset if the tail is passed.
*
* It is an error if the last accessed list node is not set; which is
* also the case when the list is empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Next data object value, or NULL if already at tail.
*/
void * ListNext(
List * pList) /* pointer to list */
{
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
if ((pList->pNodeLast = pList->pNodeLast->pNext) == pList->pHead)
{
pList->pNodeLast = NULL;
return NULL;
}
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListPrev - get previous data object value
*
* DESCRIPTION
* This routine returns the user data object value of the previous node
* with respect to the last accessed list node. The last accessed list
* node is set to the previous node, or is reset if the head is passed.
*
* It is an error if the last accessed list node is not set; which is
* also the case when the list is empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Next data object value, or NULL if already at head.
*/
void * ListPrev(
List * pList) /* pointer to list */
{
assert(pList != NULL);
validate(pList->pNodeLast != NULL, NULL);
if ((pList->pNodeLast = pList->pNodeLast->pPrev) == pList->pHead)
{
pList->pNodeLast = NULL;
return NULL;
}
else
return pList->pNodeLast->pData;
}
/******************************************************************************
*
* ListCount - report number of nodes in list
*
* DESCRIPTION
* This routine returns the number of nodes in the specified list.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Number of nodes in list.
*/
int ListCount(
List * pList) /* pointer to list */
{
assert(pList != NULL);
return pList->count;
}
/******************************************************************************
*
* ListFind - find list node
*
* DESCRIPTION
* This routine finds the node with the specified data object value. If
* nothing was found the last accessed list node is not affected,
* otherwise it is set to the found node.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Found data object value, or NULL if not found or empty list.
*/
void * ListFind(
List * pList, /* pointer to list */
void * pData) /* data object value */
{
ListNode * pNode;
assert(pList != NULL);
pNode = pList->pHead->pNext;
while (pNode != pList->pHead && pNode->pData != pData)
pNode = pNode->pNext;
if (pNode->pData == pData)
{
pList->pNodeLast = pNode;
return pData;
}
else
return NULL;
}
/******************************************************************************
*
* ListSplitBefore - split list just before last node
*
* DESCRIPTION
* This routine splits up the specified list in two parts. The split is
* made just before the last accessed list node. A new list is created
* for the part before the last accessed node. The last accessed list
* node for the original list is not affected. And the last accessed
* list node for the new list is reset.
*
* It is not allowed to pass this routine an empty list. If the list
* contains only one node, the new list will be empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to new list containing nodes before original last accessed
* list node.
*/
List * ListSplitBefore(
List * pListOrg) /* pointer to original list */
{
List * pListNew;
ListNode * pNodeOrg;
assert(pListOrg != NULL);
validate(pListOrg->count > 0, NULL);
validate(pListOrg->pNodeLast != NULL, NULL);
pListNew = malloc(sizeof(List));
pListNew->pHead = malloc(sizeof(ListNode));
pListNew->pHead->pData = NULL;
pListNew->count = 0;
pNodeOrg = pListOrg->pHead->pNext;
while (pNodeOrg != pListOrg->pNodeLast)
{
pListOrg->count--;
pListNew->count++;
pNodeOrg = pNodeOrg->pNext;
}
if (pListNew->count == 0)
{
pListNew->pHead->pPrev = pListNew->pHead->pNext = pListNew->pHead;
}
else
{
/* connect list part to new list */
pListNew->pHead->pNext = pListOrg->pHead->pNext;
pListNew->pHead->pNext->pPrev = pListNew->pHead;
pListNew->pHead->pPrev = pListOrg->pNodeLast->pPrev;
pListNew->pHead->pPrev->pNext = pListNew->pHead;
/* bind last accessed node and original dummy node together */
pListOrg->pHead->pNext = pListOrg->pNodeLast;
pListOrg->pNodeLast->pPrev = pListOrg->pHead;
}
pListNew->pNodeLast = NULL;
return pListNew;
}
/******************************************************************************
*
* ListSplitAfter - split list just after last node
*
* DESCRIPTION
* This routine splits up the specified list in two parts. The split is
* made just after the last accessed list node. A new list is created
* for the part after the last accessed node. The last accessed list
* node for the original list is not affected. And the last accessed
* list node for the new list is reset.
*
* It is not allowed to pass this routine an empty list. If the list
* contains only one node, the new list will be empty.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* Pointer to new list containing nodes after original last accessed
* list node.
*/
List * ListSplitAfter(
List * pListOrg) /* pointer to original list */
{
List * pListNew;
ListNode * pNodeOrg;
assert(pListOrg != NULL);
validate(pListOrg->count > 0, NULL);
validate(pListOrg->pNodeLast != NULL, NULL);
pListNew = malloc(sizeof(List));
pListNew->pHead = malloc(sizeof(ListNode));
pListNew->pHead->pData = NULL;
pListNew->count = 0;
pNodeOrg = pListOrg->pNodeLast->pNext;
while (pNodeOrg != pListOrg->pHead)
{
pListOrg->count--;
pListNew->count++;
pNodeOrg = pNodeOrg->pNext;
}
if (pListNew->count == 0)
{
pListNew->pHead->pPrev = pListNew->pHead->pNext = pListNew->pHead;
}
else
{
/* connect list part to new list */
pListNew->pHead->pNext = pListOrg->pNodeLast->pNext;
pListNew->pHead->pNext->pPrev = pListNew->pHead;
pListNew->pHead->pPrev = pListOrg->pHead->pPrev;
pListNew->pHead->pPrev->pNext = pListNew->pHead;
/* bind last accessed node and original dummy node together */
pListOrg->pNodeLast->pNext = pListOrg->pHead;
pListOrg->pHead->pPrev = pListOrg->pNodeLast;
}
pListNew->pNodeLast = NULL;
return pListNew;
}
/******************************************************************************
*
* ListConcat - concatenate two lists
*
* DESCRIPTION
* This routine concatenates the second list <pListAdd> to the tail of
* the first list <pListDst>. Either list (or both) may be empty. After
* the operation the <pListAdd> handle is destroyed. The last accessed
* list node of the resulting list is reset.
*
* SIDE EFFECTS
* None.
*
* RETURNS
* List pointer <pListDst> containing the nodes of both lists.
*/
List * ListConcat(
List * pListDst, /* pointer to destination list */
List * pListAdd) /* pointer to list to be added at tail */
{
assert(pListDst != NULL);
assert(pListAdd != NULL);
switch (((pListAdd->count > 0) << 1) | (pListDst->count > 0))
{
case 0:
/* both lists empty */
break;
case 1:
/* destination list not empty and add list empty */
break;
case 2:
/* destination list empty and add list not empty */
pListDst->pHead->pNext = pListAdd->pHead->pNext;
pListDst->pHead->pNext->pPrev = pListDst->pHead;
pListDst->pHead->pPrev = pListAdd->pHead->pPrev;
pListDst->pHead->pPrev->pNext = pListDst->pHead;
break;
case 3:
/* both lists not empty */
pListAdd->pHead->pPrev->pNext = pListDst->pHead;
pListDst->pHead->pPrev->pNext = pListAdd->pHead->pNext;
pListAdd->pHead->pNext->pPrev = pListDst->pHead->pPrev;
pListDst->pHead->pPrev = pListAdd->pHead->pPrev;
break;
}
pListDst->pNodeLast = NULL;
pListDst->count += pListAdd->count;
free(pListAdd->pHead);
free(pListAdd);
return pListDst;
}
/* end of List.c */
以下是 header 的基本部分:
#ifndef _LIST_H
#define _LIST_H
typedef struct _ListNode ListNode;
struct _ListNode
{
ListNode * pNext; /* next node ('down', 'after') */
ListNode * pPrev; /* next node ('up', 'before') */
void * pData; /* pointer to user data */
};
typedef struct _List List; /* doubly linked list */
struct _List
{
ListNode * pHead; /* pointer to dummy list head node */
ListNode * pNodeLast; /* pointer to last accessed node */
int count; /* number of user nodes in list */
};
我知道这是一个有点 non-standard 的答案。但我认为看到另一个实现会有所帮助。
您的模型表明 *tail 为 NULL,并且是指向将来添加的节点(不是列表中的最后一项)的指针。完成插入后,您必须 re-assign 尾巴。
Node_L *temp = newNode_L(word);
if(*list==NULL){
*tail = *list = temp;
tail = temp->next;
}
问题出在您的 insertNode_L
(...) 方法的 else
块中。应该是
(*tail)->next = temp;
*tail = temp;
在你的代码中
Node_L *t = *tail;
t->next = *tail; // This makes tail point to itself resulting in an infinite loop
*tail = temp;