C双链表分段故障

时间:2010-07-12 01:49:13

标签: c data-structures linked-list segmentation-fault

我在C中编写了自己的链表实现,它可以很好地存储值,但每当我尝试运行列表时,我都会遇到分段错误。我不确定为什么会出现这个问题,而且我花了相当多的时间试图自己完成程序,但无济于事。你能帮我找一下错误的原因吗?这是我的代码:

#include <stdlib.h>
#include <stdio.h>

// A double linkd list node
struct list
{
    struct list * prev;
    int data;
    struct list * next;
};
typedef struct list node;
node *head = NULL;
node *tail = NULL;

// Adds an item to the end of the list
void append(int item);
// Adds an item at the given index
int insert(int item, int index);
// Removes and returns an item at the specified index
int remove_node(int index);
// Gets an item at the specified index
int get(int index);
// Returns the node at the specified index
node* get_node(int idex);


// Adds an item to the end of the list
void append(int item)
{
    // If no items have been added to the list yet   
    if(head == NULL)
    {
        head = (node*) malloc(sizeof(node));
        head->prev = NULL;
        head->data = item;
        head->next = NULL;
        tail = head;
        printf("Successfully added the head\n");
    }
    // If items have previously been added to the list
    else
    {
        node *current = (node*) malloc(sizeof(node));
        printf("Successfully allocated memory for the next item\n");
        current->prev = tail;
        printf("Assigned current previous link to tail\n");
        current->data = item;
        printf("Assignd current's data value: %d\n", item);
        current->next = NULL;
        printf("Assigned current's next value to NULL\n");
        tail = current;
        printf("Assigned tail to current\n\n\n");
    }
}


// Adds an item at the given index
// Returns an int. Nonzero means there was an error
int insert(int item, int index)
{
    node *current, *new;
    current = head;

    for( ; index > 0; index--)
    {
        current = current->next;
    }

    // Make a new node and properly connect it
    new = (node*) malloc(sizeof(node));
    new->prev = current->prev;
    new->data = item;
    new->next = current;
    current->prev = new;

    return 0;
}


// Removes and returns an item at the specified index
int remove_node(int index)
{
    int ret;
    node *current = head;

    for( ; index > 0; index--)
    {
        current = current->next;
    }
    ret = current->data;

    // Connect the nodes on both sides of current
    (current->prev)->next = current->next;
    (current->next)->prev = current->prev;

    free(current);
    return ret;
}


// Gets an item at the specified index
int get(int index)
{
    return (get_node(index))->data;
}

// Returns the node at the specified index
node* get_node(int index)
{
    node *current = head;
    for( ; index > 0; index--)
    {
        current = current->next;
    }
    return current;
}


int main(void)
{
    int i;
    for(i = 0; i < 10; i++)
    {
        append(i);
    }

    node *current = head;
    for(i = 0; i < 10; i++)
    {
        printf("%d\n", current->data);
        current = current->next;
    }

    return 0;
}

5 个答案:

答案 0 :(得分:5)

在代码的这一部分:

else
{
    node *current = (node*) malloc(sizeof(node));
    printf("Successfully allocated memory for the next item\n");
    current->prev = tail;
    printf("Assigned current previous link to tail\n");
    current->data = item;
    printf("Assignd current's data value: %d\n", item);
    current->next = NULL;
    printf("Assigned current's next value to NULL\n");
    tail = current;
    printf("Assigned tail to current\n\n\n");
}

您未将tail->next设置为current,因此next将始终为NULL。一般来说,附加调试器应该很容易发现这种bug。只需逐行浏览代码,您就会在循环中看到打印出值的值,current-&gt; next为NULL。

答案 1 :(得分:5)

您的append()功能不正确 - 还需要设置tail->next = current;(在更改tail之前)。

如果insert()不是current->prev->next = new;,您的current->prev功能同样需要设置NULL;否则需要设置head = new;。它还需要处理current NULL

您的remove_node()函数需要处理current->prev和/或current->nextNULL的情况(它应更新headtail分别在那些情况下)。

如果给定索引的函数注意不要在列表的末尾运行,如果给定的索引超出范围,那么这也是一个好主意。

答案 2 :(得分:3)

直接问题:

1 /当你追加时,你不要将当前尾部的下一个指针设置为新节点。

2 /插入时,您不会检查是否已超出列表末尾。

3 /同样,在删除时,您不会检查您是否试图超出列表范围。

4 /删除时,不检查边缘情况。例如,删除当前头部将导致您尝试将(current->prev)->next设置为某些内容,即使current->prev为NULL。

5 / main中的循环以及上面的第1项是导致您的段错误的原因。这是因为head->next永远不会被设置为尾部所以,即使您创建项目,它们也不是head知道的列表。

这实际上是一种奇怪的方法,因为通常做的是你基于价值而不是指数去除。如果您要基于索引删除(或以任何方式打印或处理)一个节点,您应该处理给定索引超出列表边界的情况。

我经常发现用纸和笔绘制当前列表然后对代码进行基准测试以确保它按预期工作是有用的。有关这方面的一个很好的例子,请参阅here

通过解释您的append代码有什么问题(其中?表示未知值,!表示未知指针,顶部的前向链接,底部的后向链接):

head
    \
     NULL
         \
          tail

Append (7):

# head = (node*) malloc(sizeof(node));
head   !
    \ /
     ?      NULL
    /           \
   !             tail

# head->prev = NULL;
head   !
    \ /
     ?
    /
NULL        NULL
                \
                 tail

# head->data = item;
head   !
    \ /
     7
    /
NULL        NULL
                \
                 tail

# head->next = NULL;
head   NULL
    \ /
     7
    /
NULL        NULL
                \
                 tail

# tail = head;
head   NULL
    \ /
     7
    / \
NULL   tail

现在一切看起来还不错。您可以毫无问题地遍历前进和后退。现在让我们追加另一个值。

# Initial state.
head   NULL
    \ /
     7
    / \
NULL   tail

Append (9):

# node *current = (node*) malloc(sizeof(node));
head   NULL          !
    \ /             /
     7             ? <- curr
    / \           /
NULL   tail      !

# current->prev = tail;
head   NULL          !
    \ /             /
     7             ? <- curr
    / \___________/
NULL   tail

# current->data = item;
head   NULL          !
    \ /             /
     7             9 <- curr
    / \___________/
NULL   tail

# current->next = NULL;
head   NULL          NULL
    \ /             /
     7             9 <- curr
    / \___________/
NULL   tail

# tail = current;
head   NULL          NULL
    \ /             /
     7             9 <- curr
    / \___________/ \
NULL                 tail

现在你应该能够轻松地看到那里缺少的东西。从head到我们正在添加的节点没有链接。

这意味着从头开始的任何循环只会在列表中看到一个元素。这也意味着,如果您尝试取消引用head->next(例如使用10次迭代for循环),您将获得未定义的行为,这是导致您的段错误的原因。

要解决此问题,请备份一步并在tail->next = current;之前插入tail = current;

# current->next = NULL; // Back up a bit.
head   NULL          NULL
    \ /             /
     7             9 <- curr
    / \___________/
NULL   tail

# tail->next = current; // This is the step we're adding.
head   ___________   NULL
    \ /           \ /
     7             9 <- curr
    / \___________/
NULL   tail

# tail = current;
head   ___________   NULL
    \ /           \ /
     7             9 <- curr
    / \___________/ \
NULL                 tail

这是我们的新列表,可以在两个方向完全遍历。您可以使用相同的方法添加另一个,以验证行为是否正确。

答案 3 :(得分:0)

for( ; index > 0; index--)
{
    current = current->next;
}

可能会尝试查找NULL的下一个导致Segmentation fault的问题。如果你的意思是通过'run through'来检索列表的元素。

答案 4 :(得分:-1)

你在这里崩溃,因为current-&gt;数据为空:

node *current = head;
for(i = 0; i < 10; i++)
{
    printf("%d\n", current->data);  // here 
    current = current->next;
}

您可以使用while (current)之类的东西代替for循环。你还有另外一个问题,但这会导致你的seg错误。

您可以使用gdb找到导致错误的行:

> gcc -g list.c
> gdb a.out
(gdb) run