You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言链表实现日历规划器:预约描述无法显示问题求助

基于链表的日历规划器打印异常问题排查与修复

问题描述

使用C语言实现基于链表的日历规划器,要求按时间顺序插入预约,但打印链表时无法显示预约的描述信息。且不能修改任何结构体定义,编译命令为gcc -g -Wall -pedantic-errors main.c。

原始代码

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>

// Appointment
typedef struct
{
    time_t start;
    char *description;
} Appointment;

// List element
typedef struct Element
{
    Appointment *appointment;
    struct Element *next;
} Element;

// List of appointments
typedef struct
{
    Element *head, *tail;
} List;

void printAppointment(Appointment *appointment)
{
/*
    Print the Appointment structure
*/

    char *description = appointment->description;
    time_t startTime = appointment->start;
    printf("Appointment(%s, %ld)\n", description, startTime);
}

Appointment createAppointment(char *description, time_t start)
{
    Appointment appointment = {0};
    appointment.description = description;
    appointment.start = start;

    return appointment;
}

List createList()
/*
    Create a linked list with a head and a tail element
*/
{
    Element *head;
    Element *tail;

    head = malloc(sizeof(Element));
    tail = malloc(sizeof(Element));

    head->next = tail;

    List *list;

    list = malloc(sizeof(List));
    list->head = head;
    list->tail = tail;

    return *list;
}

void clearList(List list)
/*
    Free the list from all memory allocated, but keep the head and the tail
*/
{
    Element *temp;

    // Free elements as long as the head is not pointing to the tail
    while (list.head->next != list.tail)
    {
        // Extract the element after the head into temp, point head at the element after
        temp = list.head->next;
        list.head->next = temp->next;

        // Free the exctracted element
        free(temp);
    }
}

void debugList(List list)
/*
    Print the list to the console
*/
{

    struct Element *current = list.head->next;
    while (current->appointment != NULL)
    {
        printf("%s ", current->appointment->description);
        
        current = current->next;
    }
    printf("\n");
}

void insertElement(List list, char *description, time_t start)
/*
    Insert a new element into the list based on its value
*/
{

    printf("Inserting element %s with start time %ld\n", description, start);
    Element *new_element = malloc(sizeof(struct Element));
    Appointment new_appointment = createAppointment(description, start);
    
    new_element->appointment = &new_appointment;
    new_element->next = NULL;

    if (list.head->next == list.tail)
    {
        // List is empty, insert new element after the head
        printf("Head is pointing to tail, inserting %s in the middle\n", description);
     
        new_element->next = list.tail;
        list.head->next = new_element;
        return;
    }
    else
    {
        // Find the correct position for the new node in the list
        struct Element *current = list.head->next;
        struct Element *prev = list.head;

        int index = 0;

        while (1)
        {
            // If the new value is bigger than the current value, insert it before the current element
            if (current->appointment->start >= start)
            {
                printf("Inserting element %s at position %d\n", description, index);
                prev->next = new_element;
                new_element->next = current;
                return;
            }

            // Iterate forwards
            prev = current;
            current = current->next;
            index++;

            // If the new element is bigger than all elements, insert it at the end
            if (current == list.tail)
            {
                prev->next = new_element;
                new_element->next = list.tail;
                return;
            }
        }
    }
}

Element *findElement(List list, char *description)
{
    /*
        Find a element in the list based on its value. Returns nullptr if the element is not found.
    */
    Element *current = list.head->next;
    while (current->next != list.tail)
    {
        Appointment currentAppointment = *current->appointment;
        
        printf("Appointment(%s, %ld)\n", currentAppointment.description, currentAppointment.start);
        return NULL;
    }
    printf("Returning NULL, no element found!");
    return NULL;
}

_Bool deleteElement(List list, char *description)
{
    /*
        Find a element in the list based on its value and delete it. Returns true if the element has been deleted.
    */
    Element *current = list.head->next;
    Element *prev = list.head;

    while (current->next != list.tail)
    {
        char *currentDescription = current->appointment->description;

        if (*currentDescription == *description)
        {
            prev->next = current->next;
            free(current);
            return true;
        }
        prev = current;
        current = current->next;
    }

    return false;
}

int main()
{
    // Create the list
    List list = createList();

    // Insert some nodes
    insertElement(list, "test1", time(NULL) - 1000);
    insertElement(list, "test3", time(NULL) - 3000);
    insertElement(list, "test2", time(NULL) - 2000);
    insertElement(list, "test4", time(NULL) - 4000);
    insertElement(list, "test8", time(NULL) - 8000);

    // Print the list
    debugList(list);


    return 0;

    // Search the element with value 3
    Element *el = malloc(sizeof(Element)); 
    el = findElement(list, "test3");

    //printAppointment(el->appointment);
    //printAppointment(el->next->appointment);

    // Delete the element with value 3
    deleteElement(list, "test3");
    debugList(list);

    // clear the list
    clearList(list);

    // print the list again;
    debugList(list);
    return 0;
}

问题分析与修复步骤

1. 核心问题:局部变量地址非法引用

在insertElement函数中,new_appointment是局部栈变量,函数执行结束后会被销毁,但代码将它的地址赋值给了new_element->appointment。后续访问这个指针时,指向的是已经释放的内存,属于野指针行为,导致描述信息无法正常读取。

修复方法:动态分配Appointment内存(不修改结构体,仅调整内存分配方式):

  • 修改createAppointment返回指针类型,内部动态分配内存:
Appointment* createAppointment(char *description, time_t start)
{
    Appointment *appointment = malloc(sizeof(Appointment));
    appointment->description = description;
    appointment->start = start;

    return appointment;
}
  • 同步修改insertElement中的调用代码:
void insertElement(List list, char *description, time_t start)
{
    printf("Inserting element %s with start time %ld\n", description, start);
    Element *new_element = malloc(sizeof(struct Element));
    Appointment *new_appointment = createAppointment(description, start);
    
    new_element->appointment = new_appointment;
    new_element->next = NULL;

    // 后续插入逻辑保持不变...
}

2. debugList循环条件错误

当前循环条件while (current->appointment != NULL)会提前终止,因为链表的tail节点未初始化appointment指针(为NULL),遍历到有效节点的下一个节点即tail时会直接退出,导致最后一个有效节点可能无法打印。

正确的循环条件应判断当前节点是否为tail:

void debugList(List list)
{
    struct Element *current = list.head->next;
    while (current != list.tail)
    {
        printf("%s ", current->appointment->description);
        current = current->next;
    }
    printf("\n");
}

3. deleteElement字符串比较错误

当前用*currentDescription == *description仅比较字符串的第一个字符,无法正确匹配完整描述,需使用strcmp函数:

_Bool deleteElement(List list, char *description)
{
    Element *current = list.head->next;
    Element *prev = list.head;

    while (current != list.tail)
    {
        char *currentDescription = current->appointment->description;

        if (strcmp(currentDescription, description) == 0)
        {
            prev->next = current->next;
            free(current->appointment); // 释放Appointment的内存
            free(current);
            return true;
        }
        prev = current;
        current = current->next;
    }

    return false;
}

4. findElement逻辑错误

当前函数仅遍历第一个节点就返回NULL,无法完成全链表查找,需遍历至tail:

Element *findElement(List list, char *description)
{
    Element *current = list.head->next;
    while (current != list.tail)
    {
        if (strcmp(current->appointment->description, description) == 0)
        {
            return current;
        }
        current = current->next;
    }
    printf("Returning NULL, no element found!");
    return NULL;
}

5. clearList内存泄漏修复

原clearList仅释放Element内存,未释放每个Element指向的Appointment内存,需补充:

void clearList(List list)
{
    Element *temp;

    while (list.head->next != list.tail)
    {
        temp = list.head->next;
        list.head->next = temp->next;

        free(temp->appointment); // 释放Appointment
        free(temp);
    }
}

6. main函数冗余代码调整

main中return 0之后的代码不会执行,需将这部分代码移至return 0之前:

int main()
{
    List list = createList();

    insertElement(list, "test1", time(NULL) - 1000);
    insertElement(list, "test3", time(NULL) - 3000);
    insertElement(list, "test2", time(NULL) - 2000);
    insertElement(list, "test4", time(NULL) - 4000);
    insertElement(list, "test8", time(NULL) - 8000);

    debugList(list);

    // 查找test3
    Element *el = findElement(list, "test3");
    if (el != NULL) {
        printAppointment(el->appointment);
    }

    // 删除test3
    if (deleteElement(list, "test3")) {
        printf("Deleted test3\n");
    }
    debugList(list);

    // 清空链表
    clearList(list);

    // 再次打印链表
    debugList(list);

    // 释放list的头尾节点内存
    free(list.head);
    free(list.tail);
    return 0;
}

修复后效果

编译运行后,链表会按时间顺序打印预约描述,查找、删除功能正常工作,同时避免了野指针和内存泄漏问题。

内容的提问来源于stack exchange,提问作者noah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:01:18