【问题标题】:Doubly linked list - segfault双向链表 - 段错误
【发布时间】:2017-06-12 21:58:05
【问题描述】:

我有一个双向链表,

struct node
{
   int data;
   struct node *prev;
   struct node *next;
};

还有一个我实现的 deleteEnd 函数,

bool deleteEnd(struct node **head, int* value) {
    if (*head == NULL) return false;
    struct node* end = *head;
    while (end->next != NULL) {
        end = end->next;
    }

    if (end == *head) *head = NULL;
    else end->prev->next = NULL;

    *value = end->data;
    free(end);

    return true;
}

这给了我一个分段错误,但我不知道为什么。此时我的列表有 3 个元素 (1<->2<->5),应该删除 5

list.h

#pragma once

#include <stdbool.h>

/* doubly linked list structure */
struct node
{
   int data;
   struct node *prev;
   struct node *next;
};

struct node* create(int value);
bool insertAtBeginning(struct node **head, int value);
bool insertAtEnd(struct node **head, int value);
bool insertAfter(struct node **head, int value, int preVal);
bool deleteBeginning(struct node **head, int* value);
bool deleteEnd(struct node **head, int* value);
bool deleteSpecific(struct node **head, int value);
void display(struct node *head);

list.c

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

struct node* create(int value) {
    struct node* n = malloc(sizeof(struct node));
    if (n == NULL) return NULL;
    n->data = value;
    n->prev = NULL;
    n->next = NULL;
    return n;
}
bool insertAtBeginning(struct node **head, int value) {
    struct node* old_head = *head;
    *head = create(value);
    if (*head == NULL) return false;
    (*head)->next = old_head;
    return true;
}
bool insertAtEnd(struct node **head, int value) {
    // Get last node
    struct node* last = *head;
    while (last->next != NULL) {
        last = last->next;
    }
    // Insert after
    last->next = create(value);
    if (last->next == NULL) return false;
    else return true;
}
bool insertAfter(struct node **head, int value, int preVal) {
    // Get previous
    struct node* prev = *head;
    while (prev->data != preVal && prev->next != NULL) {
        prev = prev->next;
    }
    // Not founnd ?
    if (prev->next == NULL && prev->data != preVal) return false;

    // Insert in between
    struct node* nxt = prev->next;
    struct node* insert = create(value);
    if (insert == NULL) return false;
    prev->next = insert;
    insert->next = nxt;
    return true;
}
bool deleteBeginning(struct node **head, int* value) {
    struct node* hd = *head;
    *value = hd->data;
    *head = (*head)->next;
    free(hd);
    return true;
}
bool deleteEnd(struct node **head, int* value) {
    if (*head == NULL) return false;
    struct node* end = *head;
    while (end->next != NULL) {
        end = end->next;
    }

    if (end == *head) *head = NULL;
    else end->prev->next = NULL;

    *value = end->data;
    free(end);

    return true;
}
bool deleteSpecific(struct node **head, int value) {
    // Find node
    struct node* n = *head;
    while (n->data != value && n->next != NULL) {
        n = n->next;
    }
    // Not found ?
    if (n->next == NULL && n->data != value) return false;

    // Deleting head ?
    if (n == *head) {
        *head = (*head)->next;
        free(n);
    }
    // Delete in between
    else {
        struct node* nxt = n->next;
        struct node* prev = n->prev;
        prev->next = nxt;
        free(n);
    }
    return true;
}
void display(struct node *head) {
    if (head == NULL) {
        printf("List is Empty!!!");
    }
    else {
        printf("\nList elements are:\n");
        do {
            printf("%d ", head->data);
            head = head->next;
        }
        while(head != NULL);
        printf("\n\n");
    }
}

ma​​in.c

#include <stdio.h>
#include "list.h"

int main()
{
    int value, preVal, retVal;
    struct node *head = NULL;


    /* insert data */
    value = 2;
    printf("insert %d %s\n", value, insertAtBeginning(&head, value) ? "OK":"NOK");

    display(head);

    value = 5;
    printf("insert %d %s\n", value, insertAtEnd(&head, value) ? "OK":"NOK");

    display(head); // printf("blabla");

    value = 3;
    printf("insert %d %s\n", value, insertAtBeginning(&head, value) ? "OK":"NOK");

    display(head);

    value = 3;
    preVal = 0;
    printf("insert %d after %d %s\n", value, preVal, insertAfter(&head, value, preVal) ? "OK":"NOK");

    display(head);

    value = 1;
    preVal = 3;
    printf("insert %d after %d %s\n", value, preVal, insertAfter(&head, value, preVal) ? "OK":"NOK");

    display(head);

    /* delete data */
    retVal = deleteBeginning(&head, &value);
    printf("delete %d %s\n", value, retVal ? "OK": "NOK");
    display(head);
    retVal = deleteEnd(&head, &value);
    printf("delete %d %s\n", value, retVal ? "OK": "NOK");
    display(head);
    value = 3;
    retVal = deleteSpecific(&head, value);
    printf("delete %d %s\n", value, retVal ? "OK":"NOK");

    display(head);

    return 0;
}

【问题讨论】:

  • 函数中的哪一行出现了段错误?
  • 第9行,看旁边的评论。
  • 如果列表中只有一个节点会怎样?添加节点时是否正确设置了链接?您应该有足够的经验知道如何创建Minimal, Complete, and Verifiable Example 来向我们展示。甚至可能知道如何使用调试器逐行检查您的代码(所有),以确保它按预期工作?
  • 列表是一致的 afaik,在我的用例中有 3 个节点。
  • 与您的问题无关,但您的 insertAtBeginning 函数有一个很大的缺陷:如果 create 函数失败,那么您将丢失整个列表。

标签: c memory-management doubly-linked-list


【解决方案1】:

如果end等于head这个语句

end->prev->next = NULL; // <- segfault

导致未定义的行为,因为end-&gt;prev 等于 NULL;

我会通过以下方式定义函数

bool deleteEnd(struct node **head, int *value ) 
{
    bool success = *head != NULL;

    if ( success )
    {
        while ( ( *head )->next != NULL ) head = &( *head )->next;

        *value = ( *head )->data;

        struct node *last = *head;

        *head = NULL;

        free( last );
    }

    return success;
}

编辑:在你展示了额外的代码之后,至少已经看到了这个函数

bool insertAtBeginning(struct node **head, int value) {
    struct node* old_head = *head;
    *head = create(value);
    if (*head == NULL) return false;
    (*head)->next = old_head;
    return true;
}

是错误的,因为它没有设置old_head的数据成员prev

或者在这个函数中

bool insertAtEnd(struct node **head, int value) {
    // Get last node
    struct node* last = *head;
    while (last->next != NULL) {
        last = last->next;
    }
    // Insert after
    last->next = create(value);
    if (last->next == NULL) return false;
    else return true;
}

不检查 *head 是否等于 NULL。再次,新创建节点的数据成员prev 设置不正确。

这就是数据成员prev 的值为NULL 是函数deleteEnd 工作不正确的原因。

你应该修改你的所有函数。

【讨论】:

  • 看看我的编辑,我认为现在更清楚了(处理列表有 1 个元素)。还是同样的问题。我针对 btw 测试此功能的唯一用例是使用 3 个节点列表。
  • @PinkTurtle 表示问题是由于错误地将元素推入列表。
【解决方案2】:

你必须检查结束元素是否有前一个。如果没有,则不能将下一个元素写入前一个元素。

你缺少 if 语句。

if (end->prev)
    end->prev->next = NULL; // <- segfault

【讨论】:

  • 只要有“下一个”元素,while 循环就会循环并移动到它。
猜你喜欢
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-03-11
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 2018-07-23
  • 1970-01-01
相关资源
最近更新 更多