C中的单个链表

时间:2013-12-09 17:09:21

标签: c pointers data-structures linked-list

我正在尝试用C编写一个单链表。到目前为止,我只是得到分段错误。 我可能设置指针错误,但我无法想象如何正确地做到这一点。

该列表应该用于从最高优先级(在列表的开头)到最低优先级(在列表的末尾)排序的“处理器”。 Head应该指向第一个元素,但不知何故我做错了。

首先是代码:

struct process {
    int id;
    int priority;
    struct process *next;
}

struct process *head = NULL;

void insert(int id, int priority) {
    struct process * element = (struct process *) malloc(sizeof(struct process));

    element->id = id;
    element->priority = priority;

    while(head->next->priority >= priority)
        head = head->next;

    element->next = head->next;
    head->next = element;
    // I put here a printf to result, which leads to segmenatition fault
    // printf("%d %d\n", element->id, element->priority);
}

/* This function should return and remove element with the highest priority */
int pop() {
    struct process * element = head->next;
    if(element == NULL)
        return -1;
    head->next = element->next;
    free(element);
    return element->id;
}
/* This function should remove a element with a given id */
void popId(int id) {
    struct process *ptr = head;
    struct process *tmp = NULL;

    while(prt != NULL) {
        if(ptr->id == id) {
            ptr->next = ptr->next->next;
            tmp = ptr->next;
        } else {
            prt = ptr->next;
        }
     }
    free(tmp);
}

很遗憾,由于细分错误,我无法试用pop()popId()

任何人都可以告诉我我做错了什么?

编辑:现在,我编辑了插入功能。它看起来像这样:

void insert(int id, int priority) {
    struct process * element = (struct process *) malloc(sizeof(struct process));
    struct process * temp = head;

    element->id = id;
    element->priority = priority;

    if(head == NULL) {
        head = element; // edited due to Dukeling
        element->next = NULL;
    } else {

        while(temp->next != NULL && temp->next->priority >= priority)
            temp = temp->next;

        element->next = head->next;
        head->next = element;
    }
    // I put here a printf to result, which leads to segmenatition fault
    // printf("%d %d\n", element->id, element->priority);
}

但是我仍然得到pop()和popId()的分段错误。我在这里想念的是什么?

3 个答案:

答案 0 :(得分:3)

您不会在head中检查NULL insert是否为head

在任何功能中,您实际上都不会检查NULL是否为head。除非您想在{{1}}上放置一些虚拟元素,否则您应该简化代码。

答案 1 :(得分:3)

适用于insert

关于这些内容:

while(head->next->priority >= priority)
    head = head->next;
  • 如果headNULL,那就不行了。如果head永远不能NULL出于任何原因(例如,它有一个虚拟元素,如gruszczy所提到的那样),这实际上可能不是问题。

  • 您正在更改head,因此您每次插入时都会删除前几个元素。你可能需要一个临时变量。

  • 如果您到达列表末尾,还需要进行NULL检查。

所以,我们得到:

struct process *temp = head;
while (temp->next != NULL && temp->next->priority >= priority)
    temp = temp->next;

适用于pop

如果第一个元素不是虚拟元素,那么您应该返回head的ID,而不是head->next(并且您尝试返回已释放变量的值 - 这个是未定义的行为)。

if (head == NULL)
    return -1;
int id = head->id;
struct process *temp = head;
head = head->next;
free(temp);
return id;

适用于popId

  • 您正在查看ptr的ID,但是,如果它是我们正在查找的ID,那么您将删除下一个元素而不是ptr。你应该检查下一个人的身份证。

  • head == NULL再次需要成为特例。

  • free应该在if语句中。如果不是,您需要满足它找不到或找到具有相同ID的多个元素。

  • 如果只有一个带有该ID的元素,或者你只想删除第一个这样的元素,你应该在if语句中跳出循环。

我会留给你修理,但这是一个使用双指针的版本。

void popId(int id)
{
    struct process **ptr = &head;

    while (*ptr != NULL)
    {
        if ((*ptr)->id == id)
        {
            struct process *temp = *ptr;
            *ptr = (*ptr)->next;
            free(temp);
        }
        else
        {
            prt = &(*ptr)->next;
        }
    }
}

请注意,上面的代码不会突破if语句中的循环。如果您保证在列表中只有一个具有某个给定ID的元素,或者您只想删除第一个这样的元素,则可以添加此项。

答案 2 :(得分:1)

在访问取消引用值之前,请不要检查指针。如果指针无效(NULL或不确定),这将自动导致未定义的行为。对于下面的每个实现,请注意我们不会通过取消引用访问数据,除非指针首先被称为有效:

实施:insert()

void insert(int id, int priority) 
{
    struct process **pp = &head;
    struct process *element = malloc(sizeof(*element);
    element->id = id;
    element->priority = priority;

    while (*pp && (*pp)->priority >= priority)
        pp = &(*pp)->next;

    element->next = *pp;
    *pp = element;
}

实施:pop()

您的pop()函数似乎旨在返回弹出值。虽然这并非完全不常见,但是它具有不希望的副作用,即没有与调用者通信的机制,队列是空的,没有某种类型的哨兵值(例如(-1))在你的情况下。这是大多数队列具有top()pop()isempty()功能接口的主要原因。无论如何,假设(-1)可接受为错误条件:

int pop()
{
    struct process *tmp = head;
    int res = -1;
    if (head)
    {
        head = head->next;
        res = tmp->id;
        free(tmp);
    }
    return res;
}

实施:popId()

再一次,寻找一个特定的节点可以用一个相当简洁的算法中的指针到指针完成,由于使用了实际的物理指针而不仅仅是它们的值,为你做了自动更新:

void popId(int id) 
{
    struct process ** pp = &head, *tmp = NULL;
    while (*pp && (*pp)->id != id)
        pp = &(*pp)->next;

    if (*pp)
    {
        tmp = *pp;
        *pp = tmp->next;
        free(tmp);
    }
}

强烈建议使用调试器逐步完成这些操作,看看它们是如何工作的,尤其是insert()方法,它在看似隐藏的内容中有相当多的内容。少量代码。

祝你好运