链接列表插入无法按预期工作

时间:2017-07-16 17:19:31

标签: c

我正在编写一个函数,通过按名称字段对它们进行排序,按字母顺序将新节点放入链表结构中。这是我的程序,旨在测试它是否可以成功地将新节点插入到现有结构中:

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

#define MAX_NAME_LENGTH 100
#define MAX_JOB_LENGTH 100

struct Employee
{
    /* Employee details */
    char name[MAX_NAME_LENGTH+1]; /* name string */
    char sex; /* sex identifier, either ’M’ or ’F’ */
    int age; /* age */
    char job[MAX_JOB_LENGTH+1]; /* job string */
    /* pointers to previous and next employee structures in the linked list
     (for if you use a linked list instead of an array) */
    struct Employee *prev, *next;
};

void place_alpha(struct Employee *new, struct Employee **root);

int main(){
    struct Employee *a;
    struct Employee *c;
    struct Employee *b;
    a = malloc(sizeof(struct Employee));
    c = malloc(sizeof(struct Employee));
    b = malloc(sizeof(struct Employee));

    strcpy(a->name, "A");
    a->sex = 'F';
    a->age = 42;
    strcpy(a->job, "Optician");
    a->prev = NULL;
    a->next = c;

    strcpy(c->name, "C");
    c->sex = 'F';
    c->age = 22;
    strcpy(c->job, "Nurse");
    c->prev = a;
    c->next = NULL;

    strcpy(b->name, "B");
    b->sex = 'M';
    b->age = 34;
    strcpy(b->job, "Rockstar");
    b->prev = NULL;
    b->next = NULL;

    place_alpha(b, &a);

    if(a->prev == NULL)
    {
        printf("a->prev is correct\n");
    }else{
        printf("a->prev is INCORRECT\n");
    }

    if(a->next == b)
    {
        printf("a->next is correct\n");
    }else{
        printf("a->next is INCORRECT");
    }

    if(b->prev == a)
    {
        printf("b->prev is correct\n");
    }else{
        printf("b->prev is INCORRECT\n");
    }

    if(b->next == c)
    {
        printf("b->next is correct\n");
    }else{
        printf("b->next is INCORRECT\n");
    }

    if(c->prev == b)
    {
        printf("c->prev is correct\n");
    }else{
        printf("c->prev is INCORRECT\n");
    }

    if(c->next == NULL)
    {
        printf("c->next is correct\n");
    }else{
        printf("c->next is INCORRECT\n");
    }
}

void place_alpha(struct Employee *new, struct Employee **root) //Places a new node new into the database structure whose root is root.
{
    if(*root==NULL) //If there is no database yet.
    {
        *root = new;
        (*root)->prev = NULL;
        (*root)->next = NULL;
    }
    else
    {
        if(strcmp(new->name, (*root)->name)<=0) // if the new node comes before root alphabetically
        {
            new->next = *root;
            new->prev = (*root)->prev;
            if((*root)->prev != NULL)
            {
                (*root)->prev->next = new;
            }
            (*root)->prev = new;

            *root = new;
            return;
        }
        else if((*root)->next == NULL) // If the next node is NULL (we've reached the end of the database so new has to go here.
        {
            new->prev = *root;
            new->next = NULL;
            (*root)->next = new;
            return;
        }
        else if(strcmp(new->name, (*root)->name)>0) // If the new node comes after root alphabetically
        {
            place_alpha(new, &(*root)->next);
            return;
        }
    }
}

可悲的是,该程序不成功,如输出所示:

a->prev is correct
a->next is correct
b->prev is INCORRECT
b->next is correct
c->prev is INCORRECT
c->next is correct
Program ended with exit code: 0

我无法弄清楚原因,因为我已明确将b->next设为cc->prev设为b

1 个答案:

答案 0 :(得分:1)

这很棘手:place_alpha()函数中存在一个细微的错误:即使它不是列表的根节点,也会更新*root。这会导致指针b错误地更新。只应使用指向实际根节点的指针调用place_alpha()

我修改了你的代码,使其更具可读性和可靠性:

  • 我写了一个函数来创建一个新节点
  • 我使用calloc()strncat()保护字符串副本不会溢出。请阅读手册中的这些功能。
  • 我使用place_alpha()以与您相同的顺序将所有3个节点插入list
  • 我使用newp代替new来避免使用C代码中的C ++关键字。

请注意,必须使用指向列表头指针的指针调用place_alpha(),如果将指针传递给中间节点,则沿着prev链接返回将找到第一个节点,但是如果新员工应插入列表的头部,则不会在调用者的范围内更新根节点的地址。这就是许多程序员喜欢使用特定结构列表头的原因。

以下是更新后的代码:

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

#define MAX_NAME_LENGTH 100
#define MAX_JOB_LENGTH  100

struct Employee {
    /* Employee details */
    char name[MAX_NAME_LENGTH + 1]; /* name string */
    char sex; /* sex identifier, either 'M' or 'F' */
    int age; /* age */
    char job[MAX_JOB_LENGTH + 1]; /* job string */
    /* pointers to previous and next employee structures in the linked list
       (for if you use a linked list instead of an array) */
    struct Employee *prev, *next;
};

void place_alpha(struct Employee *new, struct Employee **root);

struct Employee *new_employee(const char *name, char sex, int age, const char *job) {
    struct Employee *newp = calloc(1, sizeof(*newp));
    if (!newp) {
        fprintf(stderr, "cannot allocate employee\n");
        exit(1);
    }
    strncat(newp->name, name, MAX_NAME_LENGTH);
    newp->sex = sex;
    newp->age = age;
    strncat(newp->job, job, MAX_JOB_LENGTH);
    newp->next = newp->prev = NULL;
    return newp;
}

int main(void) {
    struct Employee *list = NULL;
    struct Employee *a = new_employee("A", 'F', 42, "Optician");
    struct Employee *b = new_employee("B", 'M', 34, "Rockstar");
    struct Employee *c = new_employee("C", 'F', 22, "Nurse");

    place_alpha(a, &list);
    place_alpha(c, &list);
    place_alpha(b, &list);

    if (a->prev == NULL) {
        printf("a->prev is correct\n");
    } else {
        printf("a->prev is INCORRECT\n");
    }
    if (a->next == b) {
        printf("a->next is correct\n");
    } else {
        printf("a->next is INCORRECT");
    }
    if (b->prev == a) {
        printf("b->prev is correct\n");
    } else {
        printf("b->prev is INCORRECT\n");
    }
    if (b->next == c) {
        printf("b->next is correct\n");
    } else {
        printf("b->next is INCORRECT\n");
    }
    if (c->prev == b) {
        printf("c->prev is correct\n");
    } else {
        printf("c->prev is INCORRECT\n");
    }
    if (c->next == NULL) {
        printf("c->next is correct\n");
    } else {
        printf("c->next is INCORRECT\n");
    }
    return 0;
}

void place_alpha(struct Employee *newp, struct Employee **root) {
    // Insert a new node newp into the database structure whose root is root.
    struct Employee *ep;

    if (*root == NULL) { // if there is no database yet.
        newp->next = newp->prev = NULL;
        *root = newp;
        return;
    }
    if ((*root)->prev) {
        // invalid call, should only pass the root node address
        fprintf(stderr, "invalid call: place_alpha must take a pointer to the root node\n");
        return;
    }
    if (strcmp(newp->name, (*root)->name) <= 0) {
        // if the new node comes before root alphabetically
        newp->next = *root;
        newp->prev = NULL;
        newp->next->prev = newp;
        *root = newp;
        return;
    }
    for (ep = *root;; ep = ep->next) {
        if (ep->next == NULL) {
            // If the next node is NULL, we've reached the end of the list
            // so newp has to go here.
            newp->prev = ep;
            newp->next = NULL;
            newp->prev->next = newp;
            return;
        }
        if (strcmp(newp->name, ep->next->name) <= 0) {
            // The new node comes between ep and ep->next alphabetically
            newp->prev = ep;
            newp->next = ep->next;
            newp->prev->next = newp->next->prev = newp;
            return;
        }
    }
}

编辑: place_alpha有点多余,所以我清理了它并得到了一个更简单的版本:

void place_alpha(struct Employee *newp, struct Employee **root) {
    //Places a new node newp into the database structure whose root is root.
    struct Employee **link = root;
    struct Employee *last = NULL;

    while (*link && strcmp(newp->name, (*link)->name) > 0) {
        last = *link;
        link = &last->next;
    }
    newp->prev = last;
    newp->next = *link;
    if (newp->next) {
        newp->next->prev = newp;
    }
    *link = newp;
}