C 链表以错误的顺序返回值

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

我(试图)写一个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(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->valuenextPtr;
}
如果(以前的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);
    }
}