首页 > 解决方案 > 请帮我看看为什么这个程序在第二次执行时会出错

问题描述

想搭建一个小系统输入消费者信息,但是添加新信息的功能有bug。第一次可以正常添加,但第二次添加会无限添加结果。如图,有文件信息和程序。 控制台输出

先做一些处理

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

//Create structure 
struct stu_con {
    long time;
    int num; //student ID
    char name[10];
    double money;
    struct stu_con *next;
};

struct stu_con *head;//Global variable head pointer

//Create a linked list and enter the file into the linked list
struct stu_con *create(struct stu_con *head) {
    FILE *fp;
    struct stu_con *p1, *p2, *p;
    if ((fp = fopen("d:\\fee.txt", "r+")) == NULL) {   //Open a D drive file 
        printf("Cannot open file!\n");
        exit(0);
    } else {
        printf("Successfully opened the file!\n");
        head = p1 = p2 = (struct stu_con *)malloc(sizeof(struct stu_con));
        while ((fscanf(fp, "%d%d%s%lf", &p1->time, &p1->num, p1->name, &p1->money)) != EOF) {  //getting information 
            p1 = (struct stu_con *)malloc(sizeof(struct stu_con));
            p2->next = p1;
            p2 = p1;
        }
        p1->next = NULL;
        p1 = p2 = head;
        while (p1->next != NULL) {
            p2 = p1;
            p1 = p1->next;
        }
        p2->next = NULL;

        printf("The file is entered successfully!\n");
        fclose(fp);
    }
    return head;
};

//Show the contents of the file
void show(struct stu_con *head) {
    struct stu_con *p;
    p = head;
    while (p != NULL) {
        printf("%d\t%d\t%s\t%.2lf\n", p->time, p->num, p->name, p->money);
        p = p->next;
    }
}

那么问题就在这里

//Add new information 
struct stu_con *insert(struct stu_con *head, struct stu_con *bo) {
    struct stu_con *p0, *p1, *p2;

    p1 = head;
    p0 = bo;
    if (head == NULL) {
        head = p0;
        p0->next = NULL;
    } else {
        //According to the time to determine the location to join 
        while ((p0->time > p1->time) && (p1->next != NULL)) {
            p2 = p1;
            p1 = p1->next;
        }
        if (p0->time <= p1->time) {
            if (head == p1)
                head = p0;
            else
                p2->next = p0;
            p0->next = p1;
        } else {
            p1->next = p0;
            p0->next = NULL;
        }
    }   
    return head;
};

最后是main函数

//Main function 
int main() {
    struct stu_con bo;
    head = create(head); //Create a linked list 
    show(head);
again_n:  //Build a loop 
    printf("Please enter the student consumption information record you want to add:\n");
    scanf("%d%d%s%lf", &bo.time, &bo.num, bo.name, &bo.money);
    head = insert(head, &bo);
    printf("The information is entered successfully. \n"
           "Do you want to continue to enter it?(Y/N):&quot;);
    getchar();
    while (getchar() == 'Y')
        goto again_n;  //Loop 
    show(head); //Show results 
    return 0;
}

希望有人可以帮助我,谢谢

标签: cstructlinked-list

解决方案


您的代码中有多个问题:

  • 您的列表构造不正确:您总是在尝试从文件中读取更多输入之前分配一个新的未初始化节点。列表中的最后一个节点总是虚假的,在您的代码中删除它很麻烦。您应该改为将输入读入局部变量,并且仅在成功时分配一个新节点,添加到列表的尾部,并保留指向列表的第一个和最后一个元素的指针。
  • fscanf()返回成功转换的次数。您应该进行比较fscanf(...) == 4)以正确检测转换错误。
  • 该文件应该以"r","r+"是更新模式打开以供读取,这在此处使用起来相当棘手且不必要。
  • %s是一个有风险的转换fscanf()。您应该将要存储到目标数组中的最大字符数指定为%9s.
  • main()您插入一个作为局部变量的结构main而不是分配的对象。这是不好的风格并且容易出错。您应该编写一个函数来分配一个新结构并将其插入列表中并使用它create
  • 不要使用标签和条件goto,而是使用for(;;)循环和条件break语句。
  • 你应该使用%ldtime同时具有longinprintfscanf.
  • 而不是传递head和重新调整更新的值,而是传递一个指针head并返回一个成功指示器。

这是修改后的版本:

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

//Create structure
struct stu_con {
    long time;
    int num;//student ID
    char name[10];
    double money;
    struct stu_con *next;
};

// Allocate a new structure and insert it in the list
struct stu_con *insert_stu(struct stu_con **head, const struct stu_con *stu) {
    struct stu_con *new_stu = malloc(sizeof(*new_stu));
    struct stu_con **pp;
    if (new_stu) {
        *new_stu = *stu;
        /* find the insertion position in increasing order of time */
        for (pp = head; *pp && (*pp)->time <= new_stu->time; pp = &(*pp)->next)
            continue;
        new_stu->next = *pp;
        *pp = new_stu;
    }
    return new_stu;
}

// Load records from a file and insert in the list
int load_file(struct stu_con **head, const char *filename) {
    FILE *fp;

    if ((fp = fopen(filename, "r")) == NULL) {
        printf("Cannot open file %s!\n", filename);
        return 0;
    } else {
        char buf[100];
        struct stu_con stu;

        printf("Successfully opened the file %s!\n", filename);
        while (fgets(buf, sizeof buf, fp)) {
            if (sscanf(buf, "%ld%d%9s%lf", &stu.time, &stu.num, stu.name, &stu.money) == 4)
                insert_stu(head, &stu);
            else
                printf("Invalid record: %s", buf);
        }
        printf("The file %s is entered successfully!\n", filename);
        fclose(fp);
        return 1;
    }
}

//Show the contents of the file
void show(const struct stu_con *p) {
    while (p != NULL) {
        printf("%ld\t%d\t%s\t%.2f\n", p->time, p->num, p->name, p->money);
        p = p->next;
    }
}

//Main function
int main() {
    char buf[100];
    struct stu_con stu;
    struct stu_con *head = NULL;   //Local variable head pointer, empty list

    load_file(&head, "fee.txt");  // cannot use "d:\\fee.txt" for testing
    for (;;) {
        printf("Please enter the student consumption information record you want to add:\n");
        if (!fgets(buf, sizeof buf, stdin))
            break;
        if (sscanf(buf, "%ld%d%9s%lf", &stu.time, &stu.num, stu.name, &stu.money) == 4) {
            if (insert_stu(&head, &stu))
                printf("The information is entered successfully.\n");
        } else {
            printf("Invalid data: %s", buf);
        }
        printf("Do you want to continue to enter it?(Y/N):&quot;);
        if (!fgets(buf, sizeof buf, stdin) || *buf != 'Y')
            break;
    }
    // Show the resulting list
    show(head);
    // Free the list
    while (head) {
        struct stu_con *next = head->next;
        free(head);
        head = next;
    }
    return 0;
}

推荐阅读