使用尾随指针将节点插入链表C

使用尾随指针将节点插入链表C,c,C,我正在尝试创建一个按字母顺序排列的名称链接列表,我注意到这是一个半常见的问题,但是我在实现上有点困难 所以,在我看来,项目可以添加到链接列表的开头、中间或结尾,我想我正在努力将其添加到末尾 以下是我对布尔值的实现: typedef int bool; #define TRUE 1 #define FALSE 0 这是我的节点/项目/结构: typedef struct student_s Student; struct student_s { char name[MAX_NAME_S

我正在尝试创建一个按字母顺序排列的名称链接列表,我注意到这是一个半常见的问题,但是我在实现上有点困难

所以,在我看来,项目可以添加到链接列表的开头、中间或结尾,我想我正在努力将其添加到末尾

以下是我对布尔值的实现:

typedef int bool;
#define TRUE 1
#define FALSE 0
这是我的节点/项目/结构:

typedef struct student_s Student;

struct student_s {
    char name[MAX_NAME_SIZE];
    int age;
    Student* next;              // Pointer to next student in a list
};
我的比较功能:

// Compares two student structs based on their name and age, and returns true
// if student1 should come before student2 in alphabetical order
bool comesBefore(const Student* student1, const Student* student2) {
    int name_compare = strcmp(student1->name, student2->name);

    if (name_compare < 0) {
        return TRUE;
    }
    else if (name_compare == 0) {
        int age1 = student1->age;
        int age2 = student2->age;
        if (age1 < age2) {
            return TRUE;
        }
    }
    return FALSE;
}
Student* insert(Student* student, Student* list) {
    Student* curr = NULL;
    Student* prev = NULL;
    if (list == NULL) {
        printf("list == null\n");
        return student;
    }
    if (comesBefore(student, list)) {
        printf("Student comes before list\n");
        printf("Student age = %d\n", student->age);
        printf("List age = %d\n", list->age);
        student->next = list;
        return student;
    }
    for (curr = list, prev = NULL; curr != NULL && comesBefore(student, 
        curr) != TRUE; prev = curr, curr = curr->next) {
        printf("Stage 1\n\n");
        printf("curr age = %d\n", curr->age);
        printf("student age = %d\n", student->age);
        if (comesBefore(student, curr)) {
            printf("Stage 2\n");
            if (prev != NULL) {
                prev->next = student;
            }
            student->next = curr;
            break;
        }
        if ((curr->next) == NULL) {
            printf("Appended at the end of the list\n");
            curr->next = student;
            break;
        }       
    }
    return list; 

}
我的主要功能包括所有测试:

int main(void)
{
    Student* student1 = malloc(sizeof(Student));
    Student* student2 = malloc(sizeof(Student));
    Student* student3 = malloc(sizeof(Student));
    strncpy(student1->name, "AAAAA", MAX_NAME_SIZE);
    student1->age = 10;
    student1->next = NULL;
    student2->next = NULL;
    student3->next = NULL;
    strncpy((*student2).name, "BBBBB", MAX_NAME_SIZE);
    (*student2).age = 100;
    strncpy((*student3).name, "CCCC", MAX_NAME_SIZE);
    (*student3).age = 1000;
    Student* list1 = insert(student1, NULL);
    Student* list2 = insert(student3, list1);
    Student* list3 = insert(student2, list2);
    printf("head %d\n", list3->age);
    printf("second element %d\n", (list3->next)->age);
    printf("third element %d\n", ((list3->next)->next)->age);
}
问题是我不断地遇到分割错误。我认为这是当我试图访问列表中的下一个节点(curr->next)时,next被设置为NULL,但无论出于什么原因,我的if语句

    if ((curr->next) == NULL) {
        printf("Appended at the end of the list\n");
        curr->next = student;
        break;
    }   

永远不会被触发。为什么?还是我完全错了?

问题是,在一般情况下,插入函数并不能真正插入。看看循环体:

for (curr = list, prev = NULL; curr != NULL && comesBefore(student, 
    curr) != TRUE; prev = curr, curr = curr->next) {
    printf("Stage 1\n\n");
    printf("curr age = %d\n", curr->age);
    printf("student age = %d\n", student->age);
    if (comesBefore(student, curr)) {
        printf("Stage 2\n");
        if (prev != NULL) {
            prev->next = student;
        }
        student->next = curr;
        break;
    }
    if ((curr->next) == NULL) {
        printf("Appended at the end of the list\n");
        curr->next = student;
        break;
    }       
}
仅当
curr!=空值(&comesBefore)(学生,当前)!=TRUE
,因此
if(comesBefore(student,curr))
在循环中永远不会为TRUE

相反,您希望在循环终止后即循环结束后插入。如果((curr->next)==NULL)在循环中,您也不需要该
if;如果这是真的,那么循环将再次迭代,
curr
将是
NULL
prev
将是您感兴趣的指针。循环条件写得很好,只是在错误的地方做了一些事情

这将有助于:

for (curr = list, prev = NULL;
     curr != NULL && comesBefore(student, curr) != TRUE;
     prev = curr, curr = curr->next) {
    printf("Stage 1\n\n");
    printf("curr age = %d\n", curr->age);
    printf("student age = %d\n", student->age);
}

student->next = curr;
prev->next = student;

return list;
以下是此修复程序的整个功能:

Student* insert(Student* student, Student* list) {
    Student* curr = NULL;
    Student* prev = NULL;
    if (list == NULL) {
        printf("list == null\n");
        return student;
    }
    if (comesBefore(student, list)) {
        printf("Student comes before list\n");
        printf("Student age = %d\n", student->age);
        printf("List age = %d\n", list->age);
        student->next = list;
        return student;
    }
    for (curr = list, prev = NULL;
         curr != NULL && comesBefore(student, curr) != TRUE;
         prev = curr, curr = curr->next) {
        printf("Stage 1\n\n");
        printf("curr age = %d\n", curr->age);
        printf("student age = %d\n", student->age);
    }

    student->next = curr;
    prev->next = student;

    return list;
}
调试完成后,循环体将为空;您可能希望添加一条注释,说明这是故意的(我通常喜欢这样做,以便其他阅读代码的人知道这不是bug)。比如:

Student* insert(Student* student, Student* list) {
    Student* curr = NULL;
    Student* prev = NULL;
    if (list == NULL)
        return student;

    if (comesBefore(student, list)) {
        student->next = list;
        return student;
    }

    for (curr = list, prev = NULL;
         curr != NULL && comesBefore(student, curr) != TRUE;
         prev = curr, curr = curr->next)
        ; /* Intentionally left blank */

    student->next = curr;
    prev->next = student;

    return list;
}
此外,您不需要测试
prev!=NULL
,因为如果达到循环,那么我们知道
comesBefore(student,list)
为false(因为我们在代码前面进行了测试),因此循环将始终至少执行一次。对于自我文档(并确保此变量不会因将来的代码更改而被违反),您可能希望在循环之后添加一个
assert(3)
,如下所示:

Student* insert(Student* student, Student* list) {
    Student* curr = NULL;
    Student* prev = NULL;
    if (list == NULL)
        return student;

    if (comesBefore(student, list)) {
        student->next = list;
        return student;
    }

    for (curr = list, prev = NULL;
         curr != NULL && comesBefore(student, curr) != TRUE;
         prev = curr, curr = curr->next)
        ; /* Intentionally left blank */

    assert(prev != NULL);

    student->next = curr;
    prev->next = student;

    return list;
}

您需要
#包括
才能使用
断言(3)

最大名称大小的值是多少
旁注:您不应该实现自己的
bool
,如果您的编译器支持
stdbool.h
。我将投票结束这个问题,因为标准DCV'cos将在没有调试的情况下完成问题。最大名称大小为50左右,对于我使用的字符串来说肯定足够长。不知道,用户694733,谢谢!马丁,很抱歉我是一个完全的新手,但是我该如何去调试。。。对不起,为什么不试着用gdb调试insert函数呢?