双链表 - 段错误

时间:2017-06-12 21:58:05

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

我有一个双重链表,

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");
    }
}

的main.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;
}

2 个答案:

答案 0 :(得分:1)

如果end等于head this statement

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

导致未定义的行为,因为end->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;
}

错误,因为它未设置prev的数据成员old_head

或在此功能

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 :(得分:0)

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

if语句缺少。

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