C 链表以错误的顺序返回值
我(试图)写一个LinkedList,但是,当我循环列表中的所有元素时, 项目的生成顺序与插入顺序不同 比如,我这样插入它们:C 链表以错误的顺序返回值,c,memory-leaks,linked-list,C,Memory Leaks,Linked List,我(试图)写一个LinkedList,但是,当我循环列表中的所有元素时, 项目的生成顺序与插入顺序不同 比如,我这样插入它们: slist_insert(list, "red"); slist_insert(list, "green"); slist_insert(list, "blue"); slist_insert(list, "yellow"); slist_insert(list, "pink"); slist_insert(list, "purple"); slist_insert(l
slist_insert(list, "red");
slist_insert(list, "green");
slist_insert(list, "blue");
slist_insert(list, "yellow");
slist_insert(list, "pink");
slist_insert(list, "purple");
slist_insert(list, "beige");
slist_insert(list, "white");
slist_insert(list, "black");
slist_insert(list, "brown");
slist_insert(list, "fuchsia");
slist_insert(list, "aqua");
slist_insert(list, "magenta");
但在循环中,这是产生的:
green
magenta
aqua
fuchsia
brown
black
white
beige
purple
pink
yellow
blue
red
请注意,我以前没有这样做过,所以这段代码很有可能充满了与链表算法相关的基本错误:
代码本身运行良好,但有几件事困扰着我:
- 产生错误订单(如上所述)
- 必须使用宏(有更好的方法吗?)
- 即使在调用了
之后,仍然存在内存泄漏,我无法确定它来自何处slist\u destroy
非常感谢您的帮助 关于错误的项目顺序
slist\u impl\u insertl()
的逻辑错误
让我们按照您的代码进行操作:
stringlist_t* slist_impl_insertl(stringlist_t* list, const char* str, unsigned int len)
{
stringlist_t* newnode;
if(list == NULL) // if the list is empty
{
newnode = slist_createl(str, len); // create a new item
list = newnode; // insert the new item at the start of the list
return list;
}
else // if the list is not empty
{
if(list->next == NULL) // if it contains only one item
{
list = slist_insertb(list, str, len); // insert a new item at the front of the list
return list;
}
else // if it contains more than one item
{
newnode = slist_createl(str, len); // create a new node
newnode->next = (struct stringlist_t*)list->next; // insert the new node just after the first item !?!.
list->next = (struct stringlist_t*)newnode;
return list;
}
}
return list; /* not reached */
}
因此,插入过程并不总是在同一位置插入新节点。有时在开头插入,有时在第二位插入。这就解释了为什么项目的产生顺序是错误的
一个简单的修复方法是始终在列表的开头插入新节点,然后以相反的顺序生成项目。或者,您可以迭代列表,直到到达末尾(list->next==NULL
),并在最后一项之后插入新项:
stringlist_t* slist_impl_insertl(stringlist_t* list, const char* str, unsigned int len)
{
stringlist_t *iter;
if(list == NULL)
{
list = slist_createl(str, len);
}
else
{
// find the last ist item
iter = list;
while(iter->next!=NULL)
iter = iter->next;
// insert the new item at the end of the list
iter->next = slist_createl(str,len);
}
return list;
}
关于使用宏
如果列表为空(list==NULL
),则插入过程将修改列表,使其成为第一项。宏负责重新分配修改后的列表。如果不想使用宏,则必须将列表参数作为指针传递,以便可以在插入过程中直接修改它
(首先编写代码的人这样做,这样他就可以在列表中间任何地方插入一个项目,而不必编写具体的程序)
以下是不使用宏的slist\u insert()
的候选实现:
void slist_insert(stringlist_t** list, const char* str)
{
*list = slist_impl_insertl(*list, str);
}
使用此实现,您必须更改在列表中插入项目的方式:
slist_insert(&list, "red"); // note the use of '&'
关于内存泄漏
销毁过程释放存储在每个项中的字符串,这很好。但是,每个项目也是动态分配的,因此它们也需要被释放!必须临时存储列表指针,前进到下一项,然后释放存储的指针,直到到达列表的末尾
void slist_destroy(stringlist_t* list)
{
stringlist_t *temp;
while(list != NULL)
{
// free the data contained in the current list item
free(list->data);
// save the pointer to the next item
temp = slist_next(list);
// free the current item
free(list);
// continue with the next item
list = temp;
}
}
在下列情况下列出状态:
绿色->红色->空值
1) 新建节点->红色->空
2) 绿色->新节点->红色->空
新节点总是在绿色(绿色后)和红色之间插入
以及
slist\u destroy
slist\u销毁释放字符串缓冲区,而不是释放节点 这里我给你一个我自己做的链表,也许可以帮助你理解这个数据结构(我放了插入和删除节点的函数)
void插入(LISTNODEPTR*sPtr,字符项)
{
LISTNODEPTR-previousPtr、currentPtr、newPtr;
newPtr=malloc(sizeof(LISTNODE));
如果(newPtr!=NULL)
{
newPtr->value=物料;
newPtr->nextPtr=NULL;
currentPtr=*sPtr;
previousPtr=NULL;
while(currentPtr!=NULL&¤tPtr->value- nextPtr;
}
如果(以前的ptr==NULL)
{
newPtr->nextPtr=*sPtr;
*sPtr=newPtr;
}
其他的
{
上一个PTR->下一个PTR=newPtr;
newPtr->nextPtr=currentPtr;
}
}
其他的
{
printf(“没有可用内存\n”);
}
}
作废删除(LISTNODEPTR*sPtr,字符项)
{
LISTNODEPTR currentPtr、previousPtr、tempPtr;
如果((*sPtr)->值==项目)
{
temptr=*sPtr;
*sPtr=(*sPtr)->NEXTPTTR;
免费(tempPtr);
}
其他的
{
previousPtr=NULL;
currentPtr=*sPtr;
while(currentPtr!=NULL&¤tPtr->value!=item)
{
先前的PTR=当前的PTR;
currentPtr=currentPtr->nextPtr;
}
temptr=当前ptr;
上一个PTR->nextPtr=当前PTR->nextPtr;
免费(tempPtr);
}
}
代码太多了。尝试自己进一步调试,但首先只添加一个元素来运行代码,然后只添加两个元素,然后只添加三个元素。确保删除在上述三种情况下都有效。您应该能够使用笔和纸跟踪您的分配。@Mat哦,我已经用valgrind(分别使用--leak check=full
)和gdb调试了代码,但都没有用。即使循环内部(分配到下一个列表之前)的slist\u destroy
中有一个额外的free(list)
,也不会关闭内存泄漏。这就是问题所在。我很抱歉看到你仍然认为这是家庭作业,真的。我已经相应地使用了你的建议,结果如下:。非常感谢您的帮助,非常感谢。@hiobs:重写得不错。不过,还有一个小问题:在slist_destroy()
中,在释放指针(if(head->data!=NULL)free(head->data);
。这是多余的,free()将负责<代码> null 指针本身。感谢抬头,但该检查是针对<代码>免费>代码>不检查指针是否为代码> null >代码>。我相信过去是Windows(VisualStudio)的情况,但认为此语句是非事实的。
newnode = slist_createl(str, len);
newnode->next = (struct stringlist_t*)list->next;//1)
list->next = (struct stringlist_t*)newnode;//2)
return list;
void insert(LISTNODEPTR* sPtr, char item)
{
LISTNODEPTR previousPtr,currentPtr,newPtr;
newPtr = malloc(sizeof(LISTNODE));
if(newPtr != NULL)
{
newPtr->value = item;
newPtr->nextPtr = NULL;
currentPtr = *sPtr;
previousPtr = NULL;
while(currentPtr != NULL && currentPtr->value < item)
{
previousPtr = currentPtr;
currentPtr = currentPtr->nextPtr;
}
if(previousPtr == NULL)
{
newPtr->nextPtr = *sPtr;
*sPtr = newPtr;
}
else
{
previousPtr->nextPtr = newPtr;
newPtr->nextPtr = currentPtr;
}
}
else
{
printf("No memory available\n");
}
}
void delete(LISTNODEPTR* sPtr,char item)
{
LISTNODEPTR currentPtr,previousPtr,tempPtr;
if((*sPtr)->value == item)
{
tempPtr = *sPtr;
*sPtr = (*sPtr)->nextPtr;
free(tempPtr);
}
else
{
previousPtr = NULL;
currentPtr = *sPtr;
while(currentPtr != NULL && currentPtr->value != item)
{
previousPtr = currentPtr;
currentPtr = currentPtr->nextPtr;
}
tempPtr = currentPtr;
previousPtr->nextPtr = currentPtr->nextPtr;
free(tempPtr);
}
}