我对编程还很陌生,我正在学习C。我试图构建一个功能齐全的双链表模块,但是在获取最后一个元素时遇到了段错误。
我的功能一样
void get_last(struct Node *node)
{
if (node)
{
while (node->next)
{
node = node->next;
}
}
printf("%d",node->prev->value);
}
现在,每次运行它都会引发分段错误,尽管我发现这在概念上是正确的,但由于在循环结束时node最终将指向NULL,并且我的代码将打印上一项的值,因此我无法成功调试它。如果我的想法不对,请纠正我。谢谢
编辑:我已经使用"%d"
,printf()
因为值是我创建的链接列表中的整数值
编辑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;
}
}
将printf语句移动到该if (node)
块内,否则,如果使用空指针调用,则可以保证有段错误。
接下来,大概是要打印列表中最后一个条目的值,而不是获取最后一个条目。
从您的代码中,您选择了在最后一个条目中设置next
为NULL
(可能是在列表头中设置prev
为NULL
?)。如果是这样,则与单链接列表相比,双链接列表的主要优势之一便消失了(不必遍历列表以查找最后一项)。
相反,如果您有一个固定的“ head”节点,并且您的最后一个项目在中指向它next
,而head的prev
指向最后一个项目,那么您可以使用一条语句来获取最后一个项目:
last = head.prev;
的迭代(例如线性搜索)可以在时终止node->next == &head
。
但是,您现有的代码将按以下方式退出
node == NULL
),则会在printf
node
为非null,但是node->next == NULL
大概node->prev == NULL
会再次出现段错误。while
终止正确,则只有在出现segfault的情况node->prev
下NULL
(这表明您的错误也可能在于该add
方法中。在实现任何数据结构或任何通用“类”(即使它在C中)时,实现定义的一部分需要是一组不变式。这些都是您的实现始终保证为真的事情。
如果是双向链表,则可能是
next
和prev
)和一个有效负载组成。head
。(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)
并使用它来定义自己的数据Node
结构。请注意,我在evry节点中包括对列表头的引用。这允许实现检测违反不变量的尝试(例如删除head
元素)。
typedef struct
{
dll_links link;
int value;
} Node;
现在,给定a Node *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);
...
}
遍历列表,提取第一个或最后一个条目等,作为练习。但是,考虑到附加的条目插入之前的头,在前面插入之前,插入head->next
的head
元素立刻知道哪些节点开头和列表的末尾,等等。
使用该dll_links
方法意味着,任何操作都不需要了解有关实际节点结构和有效负载的任何信息。这些功能可以链接任何内容(包括异类列表)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句