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

C语言链表头指针异常移动求助:从文件构建链表失败

C语言链表构建问题:从文件读取数据时的节点连接错误

我是编程初学者,编写了一个从文本文件读取数据构建C语言链表的程序,但遇到问题:添加新节点时head指针不断移动。尝试修改后,又出现头指针无法连接到第二个节点的新问题。

初始代码

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

//Visitor information
struct visitData
{
    char *SrNo;
    char *ticketNo;
    char *Name;
    char *ID;
};

//Ticket linked list
struct ticket
{
    struct visitData visitor;
    struct ticket *nextPtr;
};

int main() { //main

    FILE *fPtr = NULL;
    fPtr = fopen("Tickets.txt", "a+");
    if (fPtr == NULL)
    {
        printf("Error, please try again.\n");
        return 0;
    }

    FILE *outfPtr = NULL;
    outfPtr = fopen("output.txt", "w");
    if (outfPtr == NULL)
    {
        puts("Error, could not create output file, please try again.");
        return 0;
    }

    else {

        struct ticket *head = NULL;

        struct ticket *new = NULL;

        struct visitData *nextPtr = NULL;


        char sr[2];
        char ticketNumber[20];
        char name[30];
        char id[24];


        rewind(fPtr);

        if (fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id) == 4)
        {

            new = malloc(sizeof(struct ticket));
            
            new->visitor.SrNo = strdup(sr);
            new->visitor.ticketNo = strdup(ticketNumber);
            new->visitor.Name = strdup(name);
            new->visitor.ID = strdup(id);
            new->nextPtr = NULL;
            head = new;
            
            printf("%s\n", head->visitor.SrNo);

            while (!feof(fPtr))
            {

                if (fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id) == 4)

                    new->visitor.SrNo = sr;                       
                    new->visitor.ticketNo = ticketNumber;
                    new->visitor.Name = name;
                    new->visitor.ID = id;
                    
                    new->nextPtr = malloc(sizeof(struct ticket));
                    new = new->nextPtr;
                    new->nextPtr = NULL;
                }
                

            }
            
            
        }
        else
        {
            printf("%d",fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id));
            new->visitor.SrNo = "0";
        }

        if (head == NULL)
        {
            printf("List is empty\n");
        }
        struct ticket *Ptr = NULL;
        Ptr = head;
        printf("%s\n", Ptr->visitor.SrNo);
        while (Ptr != NULL)
        {
            printf("%s %s\n", Ptr->visitor.SrNo, Ptr->visitor.ticketNo);
            Ptr = Ptr->nextPtr;
        }
    }
}

修改后的代码

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

//Visitor information
struct visitData
{
    char *SrNo;
    char *ticketNo;
    char *Name;
    char *ID;
};

//Ticket linked list
struct ticket
{
    struct visitData visitor;
    struct ticket *nextPtr;
};

int main() { //main

    FILE *fPtr = NULL;
    fPtr = fopen("Tickets.txt", "a+");
    if (fPtr == NULL)
    {
        printf("Error, please try again.\n");
        return 0;
    }

    FILE *outfPtr = NULL;
    outfPtr = fopen("output.txt", "w");
    if (outfPtr == NULL)
    {
        puts("Error, could not create output file, please try again.");
        return 0;
    }

    else {

        struct ticket *head = NULL;

        struct ticket *new = NULL;

        struct visitData *nextPtr = NULL;


        char sr[2];
        char ticketNumber[20];
        char name[30];
        char id[24];


        rewind(fPtr);

        if (fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id) == 4)
        {

            new = malloc(sizeof(struct ticket));
            
            new->visitor.SrNo = strdup(sr);
            new->visitor.ticketNo = strdup(ticketNumber);
            new->visitor.Name = strdup(name);
            new->visitor.ID = strdup(id);
            new->nextPtr = NULL;
            head = new;
            
            printf("%s\n", head->visitor.SrNo);

            while (!feof(fPtr))
            {

                if (fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id) == 4)
                {
                    new = malloc(sizeof(struct ticket));
                    new->visitor.SrNo = sr;                       
                    new->visitor.ticketNo = ticketNumber;
                    new->visitor.Name = name;
                    new->visitor.ID = id;
                    
                    new->nextPtr = malloc(sizeof(struct ticket));
                    new = new->nextPtr;
                    new->nextPtr = NULL;
                }
                

            }
            
            
        }
        else
        {
            printf("%d",fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id));
            new->visitor.SrNo = "0";
        }

        if (head == NULL)
        {
            printf("List is empty\n");
        }
        struct ticket *Ptr = NULL;
        Ptr = head;
        printf("%s\n", Ptr->visitor.SrNo);
        while (Ptr != NULL)
        {
            printf("%s %s\n", Ptr->visitor.SrNo, Ptr->visitor.ticketNo);
            Ptr = Ptr->nextPtr;
        }
    }
}

问题分析与解决方案

初始代码的核心问题

  1. 节点内容被覆盖:循环中没有为新数据分配新节点,而是直接修改当前new节点的字段,导致之前的节点数据被覆盖,看起来像head指针在移动。
  2. if语句无大括号:if(fscanf(...))后面没有加{},导致后续代码不管fscanf是否成功都会执行,逻辑混乱。
  3. 野指针风险:修改后的代码中直接将栈数组(如sr)的地址赋值给结构体指针,栈内存会在函数结束后释放,导致指针指向无效内存。

修改后代码的新问题

新分配了节点,但没有将上一个节点的nextPtr指向新节点,所有新节点都是孤立的,head始终只指向第一个节点,后续节点无法被访问。另外还多分配了一个空节点,导致链表末尾出现无效节点。

修正后的完整代码

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

// 访客信息结构体
struct visitData
{
    char *SrNo;
    char *ticketNo;
    char *Name;
    char *ID;
};

// 链表节点结构体
struct ticket
{
    struct visitData visitor;
    struct ticket *nextPtr;
};

// 释放链表内存
void freeList(struct ticket *head) {
    struct ticket *temp;
    while (head != NULL) {
        temp = head;
        head = head->nextPtr;
        // 释放字符串内存
        free(temp->visitor.SrNo);
        free(temp->visitor.ticketNo);
        free(temp->visitor.Name);
        free(temp->visitor.ID);
        // 释放节点内存
        free(temp);
    }
}

int main() {
    FILE *fPtr = fopen("Tickets.txt", "r"); // 只读模式即可,不需要a+
    if (fPtr == NULL)
    {
        printf("Error opening file.\n");
        return 1;
    }

    FILE *outfPtr = fopen("output.txt", "w");
    if (outfPtr == NULL)
    {
        puts("Error creating output file.");
        fclose(fPtr);
        return 1;
    }

    struct ticket *head = NULL;
    struct ticket *tail = NULL; // 用tail跟踪链表末尾
    struct ticket *newNode = NULL;

    char sr[3]; // 留一位给字符串结束符
    char ticketNumber[21];
    char name[31];
    char id[25];

    // 用fscanf返回值控制循环,避免feof的陷阱
    while (fscanf(fPtr, "%2s%20s%29s%24s", sr, ticketNumber, name, id) == 4)
    {
        // 分配新节点
        newNode = malloc(sizeof(struct ticket));
        if (newNode == NULL) {
            printf("Memory allocation failed.\n");
            freeList(head);
            fclose(fPtr);
            fclose(outfPtr);
            return 1;
        }
        // 复制字符串,避免野指针
        newNode->visitor.SrNo = strdup(sr);
        newNode->visitor.ticketNo = strdup(ticketNumber);
        newNode->visitor.Name = strdup(name);
        newNode->visitor.ID = strdup(id);
        newNode->nextPtr = NULL;

        if (head == NULL) {
            // 链表为空时,head和tail都指向第一个节点
            head = newNode;
            tail = newNode;
        } else {
            // 将tail的next指向新节点,更新tail
            tail->nextPtr = newNode;
            tail = newNode;
        }
    }

    // 打印链表内容
    struct ticket *current = head;
    if (current == NULL) {
        printf("List is empty.\n");
    } else {
        while (current != NULL) {
            printf("%s %s\n", current->visitor.SrNo, current->visitor.ticketNo);
            fprintf(outfPtr, "%s %s %s %s\n", current->visitor.SrNo, current->visitor.ticketNo, current->visitor.Name, current->visitor.ID);
            current = current->nextPtr;
        }
    }

    // 清理资源
    freeList(head);
    fclose(fPtr);
    fclose(outfPtr);
    return 0;
}

关键修正点

  • 用tail指针管理链表末尾:每次新节点创建后,将上一个节点的nextPtr指向它,确保链表连续。
  • 正确的循环控制:直接用fscanf的返回值判断是否读取到有效数据,避免feof的常见陷阱。
  • 安全的字符串处理:所有字符串都用strdup分配堆内存,避免栈内存失效导致的野指针。
  • 资源清理:添加了链表内存释放函数,避免内存泄漏;及时关闭文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:50:25