我查找双链表中最后一个元素的代码是抛出分段错误 我对编程很熟悉,我在路上学习C。我正在尝试构建一个全功能的双链表模块,但是在获取最后一个元素时,我得到了一个分割错误。

我查找双链表中最后一个元素的代码是抛出分段错误 我对编程很熟悉,我在路上学习C。我正在尝试构建一个全功能的双链表模块,但是在获取最后一个元素时,我得到了一个分割错误。,c,segmentation-fault,doubly-linked-list,C,Segmentation Fault,Doubly Linked List,我的功能相同 void get_last(struct Node *node) { if (node) { while (node->next) { node = node->next; } } printf("%d",node->prev->value); } 现在,每次我运行它时,它都会抛出一个分段错误,我无法成功地调试它,尽管我发现这在概念上是正确的,因为当循环结

我的功能相同

void get_last(struct Node *node)
{
    if (node)
    {
        while (node->next)
        {
           node = node->next;
        }
    }
    printf("%d",node->prev->value);
}
现在,每次我运行它时,它都会抛出一个分段错误,我无法成功地调试它,尽管我发现这在概念上是正确的,因为当循环结束时,节点最终将指向NULL,我的代码将打印上一项的值。 如果我的观念错了,请纠正我。 谢谢

编辑:我在
printf()
中使用了
“%d”
,因为这些值在我创建的链接列表中是整数值

编辑1:以下是创建链表的代码:

void create_list(struct Node *node)
{
    int i;

    first.next=NULL;
    first.prev=NULL;

    node=&first;

    for(i=1;i <=10;i++)
    {
        node->next=(struct Node *)malloc(sizeof(struct Node));
        node->next->prev=node;
        node=node->next;

        node->value=rand()%20;
        node->next=NULL;
    }
}
void创建列表(结构节点*Node)
{
int i;
first.next=NULL;
第一,prev=NULL;
node=&first;
对于(i=1;i next=(结构节点*)malloc(sizeof(结构节点));
节点->下一步->上一步=节点;
节点=节点->下一步;
节点->值=rand()%20;
节点->下一步=空;
}
}

将printf语句移动到
if(node)
块内,否则,如果使用空指针调用,则会出现保证segfault

接下来,这可能意味着打印列表中最后一个条目的值,而不是获取最后一个条目

从您的代码中,您选择在最后一个条目中将
next
设置为
NULL
(这可能意味着您在列表头中将
prev
设置为
NULL
)。如果这样,您就失去了双链接列表相对于单链接列表的主要优势之一(不必遍历列表以查找最后一项

相反,如果您有一个固定的“head”节点,并且在
next
中最后一个项目指向它,并且head的
prev
指向最后一个项目,那么您可以使用一条语句获取最后一个项目:

last = head.prev;
迭代(例如线性搜索)可以在
节点->下一步==&head
时终止

但是,您的代码将按如下方式退出

  • 如果列表为空(
    node==NULL
    ),它将在
    printf
  • 如果
    node
    为非空,但
    node->next==null
    则假定
    node->prev==null
    ,因此它将再次出现故障
  • 如果列表中有一些条目,并且
    while
    正确终止,则只有当
    节点->prev
    NULL
    时,它才会segfault(这表明您的错误也可能存在于
    添加
    方法中)
当实现任何数据结构或任何通用“类”(即使是C语言)时,实现的部分定义需要是一组不变量。这些是您的实现保证始终为真的

在双链接列表的情况下,这些可能是

  • 列表中的节点由两个指针(
    next
    prev
    )和一个有效负载组成
  • 每个列表都有一个众所周知的节点,称为
  • 头部节点没有有效负载(关联值)
  • 对于列表中的所有节点,
    (prev!=NULL)&(next!=NULL)
  • 对于任何节点
    n
    n->prev->next==n
    n->next->prev==n
  • 在一个空列表中,
    (head->next==head->prev)&&(head->next==head)
因此,您可以为任何列表中的链接定义基本的、通用的结构

// Forward declaration so the structure can point to itself
struct dll_links;

// The actual overhead of a DLL
typedef struct dll_links
{
    struct dll_links *next;
    struct dll_links *prev;
    struct dll_links *head;
} dll_links, dll_head;

// A simple macro to cast linkable datastructures as a set of links.
#define AS_LINKS(n) ((dll_links *) n)
并使用它定义您自己的
节点
数据结构 evry节点中的head。这允许实现检测违反不变量的尝试(例如删除
head
元素)

现在,给定一个
节点*n
,您可以通过
n->link.prev
n->link.next
访问链接

因此,鉴于上述情况,初始化新列表的函数将是

void dll_init(dll_head *list)
{
    list->next = list.prev = list;
    list->head = list;
}
并将用于:

...
dll_head myList;
dll_init(&myList);
...
请注意,这将强制执行所有不变量

对双链接列表执行的其他操作只有插入和删除

// Insert a new node in front of an existing one
void dll_insert(dll_links *newNode, dll_links *before)
{
    dll_links *orignalPrevious = before->prev;

    // First set up the links in the new node
    newNode->prev = originalPrevious;
    newNode->next = before;
    newNode->head = before->head;

    // Now link it in by adjusting the pointers of the surrounding nodes
    before->prev = newNode;
    originalPrevious->next = newNode;
}

// Remove a specified node from a list (and return it)
dll_links *dll_remove(dll_links *node)
{
    // Check the assertion that you can not remove the head element
    assert (node->head != node);

    dll_links *successor = node->next;
    dll_links *predecessor = node->prev;

    // Remove the element from the list
    predecessor->next = successor;
    successor->prev = predecessor;

    // Ensure no dangling pointers in the removed element
    node->next = node->prev = node->head = NULL;
    return node;
}
使用这三个函数,您的
create_list
函数如下所示:

void create_list(dll_head *list)
{
    int i;

    dll_init(list);

    for(i=1;i <=10;i++)
    {
        Node *node = malloc(sizeof(struct Node));
        node->value=rand()%20;
        dll_insert(AS_LINKS(node), list); // Append the new node to the list
    }
}

...
{
    dll_head myList;
    create_list(&myList);
    ...
}
void创建列表(dll头*列表)
{
int i;
dll_init(列表);
对于(i=1;i值=rand()%20;
dll_insert(作为链接(节点),列表);//将新节点附加到列表中
}
}
...
{
dll_头myList;
创建_列表(&myList);
...
}

迭代遍历列表,提取第一个或最后一个条目等,作为练习。 追加条目是在标题之前插入,在前面插入是在

标题->下一步之前插入,
head
元素立即知道哪些节点位于列表的开头和结尾,依此类推

而使用
dll\u链接
方法意味着所有操作都不需要知道任何关于实际操作的信息
节点结构和负载。这些函数可以链接任何东西(包括异构列表)。

不要使用这种语法
printf(“%d”,node->prev->value);
这样做不好

 struct Node *previous = node->prev;
 if (previous != NULL) 
     printf("%d\n", previous->value); 
更好,因为当
node
是第一个节点时,
node->prev==NULL
,您将遇到问题


另外,您的最后一个元素是
node
,因为您一直在迭代,直到
node->next==NULL
,这应该发生在最后一个元素,但是您的代码无法解释您描述的行为,需要更多的解释,因为没有足够的代码来说明。我们如何知道
node->prev
不是NULL?请进行更多的调试,并找出确切的原因seg错误来自。不要使用这种语法
printf(“%d”,node->prev->value);
这样做不好,
struct node*previous=node->prev;if(previous)printf(“%d\n”,previous->value);
更好。最后一个元素是
node
be
 struct Node *previous = node->prev;
 if (previous != NULL) 
     printf("%d\n", previous->value);