从文件读取后打印链接列表导致的细分错误

时间:2018-10-08 02:33:40

标签: c linked-list segmentation-fault printf

我几乎已经完成了C程序来添加,查看,保存和加载患者详细信息。我已经完成了添加,保存和加载,但似乎无法正确实现保存功能。

该程序将允许用户添加患者详细信息,然后将详细信息保存到文本文件数据库中。然后,用户可以退出程序,启动程序并加载文本文件数据库。最后,用户将可以在程序中查看患者详细信息。

在我的代码中,我还添加了一些代码,以在读取文本文件后打印出内容,该文件可以正常工作,并将所有患者详细信息打印到终端上。但是,从文本文件读取后,我尝试使用“查看患者”功能并遇到分割错误。任何帮助,将不胜感激。谢谢!

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

#define DB_NAME "database"

struct dob
{
int day, month, year;
};
typedef struct dob dob_t;

struct medicine
{
int medicine_id;
char medicine_name[100];
};
typedef struct medicine medicine_t;

struct patient
{
    int patient_id;
    dob_t date_db;
    char patient_name[20];
    medicine_t patient_med;
    struct patient* nextp; 

};
typedef struct patient patient_t;

void print_menu (void);
patient_t* add_patients (patient_t* patient_headp, patient_t* temp, patient_t* patient_currentp, int num_patients);
void view_patients (patient_t* patient_currentp, patient_t* patient_headp);
void save_patients (patient_t* patient_currentp, patient_t* patient_headp);
patient_t* read_patients (patient_t* patient_currentp, patient_t* patient_headp, patient_t* temp);


int main (void){
patient_t* patient_headp = NULL;
patient_t* temp = NULL;
patient_t* patient_currentp = NULL;
int option_picked = 0;
int num_patients = 0;
while(option_picked != 5)
{
    print_menu ();
    scanf("%d", &option_picked);
    if (option_picked == 1){
        patient_headp = add_patients(patient_headp, temp, patient_currentp, num_patients);
    }


    else if (option_picked == 2){
        view_patients (patient_currentp, patient_headp);
    }


    else if (option_picked == 3){
        save_patients (patient_currentp, patient_headp);
    }


    else if (option_picked == 4){
        patient_headp = read_patients (patient_currentp, patient_headp, temp);
    }
}   
return 0;
}

void print_menu (void)
{
printf("\n"
"1. add a patient\n"
"2. display all patients\n"
"3. save the patients to the database file\n"
"4. load the patients from the database file\n"
"5. exit the program\n"
"Enter choice (number between 1-5)>\n");
}

patient_t* add_patients (patient_t* patient_headp, patient_t* temp, patient_t* patient_currentp, int num_patients){
char choice;
do
{
    temp = (patient_t*) malloc(sizeof(patient_t));
    if (temp == NULL){
        printf("Error allocating memory\n");
    }
    printf("Enter Patient ID: ");
    scanf("%d", &temp->patient_id);
    printf("Enter Patient DOB(DD MM YY): ");
    scanf("%d %d %d", &temp->date_db.day, &temp->date_db.month, 
    &temp->date_db.year);
    printf("Enter Patient Name: ");
    scanf("%s", temp->patient_name);
    printf("Enter Patient Medicine Prescription: ");
    scanf("%s", temp->patient_med.medicine_name);
    printf("Enter Patient Medicine Prescription ID: ");
    scanf("%d", &temp->patient_med.medicine_id);
    temp->nextp = NULL;
    if(patient_headp == NULL){
        patient_headp = temp;
    }
    else{
        patient_currentp = patient_headp;
        while(patient_currentp->nextp != NULL){
            patient_currentp = patient_currentp->nextp;
        }
        patient_currentp->nextp = temp;
    }
    printf("Add more patients? (Y/N) ");
    scanf(" %c", &choice);
    num_patients++;
} 
while (choice == 'Y');
return patient_headp;
}


void view_patients (patient_t* patient_currentp, patient_t* patient_headp){
    /*patient_currentp = (patient_t*) malloc(sizeof(patient_t));
    if (patient_currentp == NULL){
        printf("Error allocating memory\n");
    }
    patient_currentp = patient_headp;
    do{
        printf("%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id, 
        patient_currentp->date_db.day, patient_currentp->date_db.month, 
        patient_currentp->date_db.year, patient_currentp->patient_name, 
        patient_currentp->patient_med.medicine_name, 
        patient_currentp->patient_med.medicine_id);
        patient_currentp = patient_currentp->nextp;
    }while(patient_currentp->nextp != NULL);*/
    printf("%05d %02d/%02d/%02d %s %s %d\n", patient_headp->patient_id, 
        patient_headp->date_db.day, patient_headp->date_db.month, 
        patient_headp->date_db.year, patient_headp->patient_name, 
        patient_headp->patient_med.medicine_name, 
        patient_headp->patient_med.medicine_id);
}


void save_patients (patient_t* patient_currentp, patient_t* patient_headp){
    FILE *output = fopen(DB_NAME, "a");
    if (output == NULL){
        printf("Failed to open file\n");
    }
    patient_currentp = patient_headp;
    do{
        fprintf(output, "%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id, 
        patient_currentp->date_db.day, patient_currentp->date_db.month, 
        patient_currentp->date_db.year, patient_currentp->patient_name, 
        patient_currentp->patient_med.medicine_name, 
        patient_currentp->patient_med.medicine_id);

        patient_currentp = patient_currentp->nextp;
    }while(patient_currentp != NULL);
    fclose(output);
}


patient_t* read_patients (patient_t* patient_currentp, patient_t* patient_headp, patient_t* temp){
    FILE *input = fopen(DB_NAME, "r");
    if (input == NULL){
        printf("Failed to open file\n");
    }
    do{
        temp = (patient_t*) malloc(sizeof(patient_t));
        if (temp == NULL){
            printf("Error allocating memory\n");
        }
        while ((fscanf(input, "%05d %02d/%02d/%02d %s %s %d", 
        &temp->patient_id, &temp->date_db.day, 
        &temp->date_db.month, &temp->date_db.year, 
        temp->patient_name, 
        temp->patient_med.medicine_name, 
        &temp->patient_med.medicine_id)) != EOF)

        printf("%05d %02d/%02d/%02d %s %s %d\n", temp->patient_id, 
        temp->date_db.day, temp->date_db.month, 
        temp->date_db.year, temp->patient_name, 
        temp->patient_med.medicine_name, 
        temp->patient_med.medicine_id);

        temp->nextp = NULL;
        if(patient_headp == NULL){
            patient_headp = temp;
        }
        else{
            patient_currentp = patient_headp;
            while(patient_currentp->nextp != NULL){
                patient_currentp = patient_currentp->nextp;
            }
            patient_currentp->nextp = temp;
        }
    }while(patient_currentp != NULL);
return patient_headp;
}

2 个答案:

答案 0 :(得分:1)

您的问题的现实是(1)如果没有输入验证,那么潜在的错误源太多,可能导致不确定的行为和细分错误,很难确定(2)每当函数中列表的地址可能发生更改(例如第一个节点发生更改)时,您都需要传递 patient_headp的地址,以便函数接收实际的列表指针,而不是包含列表地址的指针的副本,以及(3)您的read_patients()无法正常工作(出于多种原因),但是从根本上来说,因为设置patient_currentp = patient_currentp->nextp;可以保证while(patient_currentp != NULL);测试为假。 / p>

没有理由将patient_currentp作为参数传递。没有理由以当前形式传递num_patients,该参数未使用,并且要使它有用,您需要传递一个指向num_patients的指针,以便可以在添加和读取过程中对其进行更新功能,并使更新的计数可以在调用功能中使用。

在我们什至没有看代码之前,如果发生 matching input 失败,则用scanf进行用户输入会引起一些陷阱。为了甚至开始正确使用scanf,您每次都必须验证退货。这意味着处理EOF matching input 失败,并处理有效的输入大小写。至少,在使用输入之前,您必须检查预期的转换次数。

如果出现“ 匹配失败”,将从输入缓冲区中提取字符停止,使有问题的字符保持未读状态,只是在等待下一次尝试读取时咬住您。为了促进从匹配失败中恢复,您可以从输入缓冲区中删除有问题的字符。 stdin的常规方法是简单地使用getchar()进行读取,直到遇到'\n'EOF。简短的帮助功能使生活更轻松,例如

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

除了验证问题外,您可能会发现通过返回指向添加的节点(或失败时为add_patients())的指针来指示NULL函数中成功/失败的健壮性。通过在函数中循环以添加多个患者而不是简单地从菜单中再次调用该函数,这会有些复杂。无论如何,返回指向最后添加的节点的指针都同样有效。

无法使用此答案分配的字符逐步解决代码中的每个问题。相反,我整理了您的代码,以解决每个用户输入的验证问题,从函数声明中删除了不必要的参数,并将save_patients()read_patients()的返回类型更改为{{1} },以在成功进行写入或读取时提供int,否则提供1

注意,对0中的fclose进行验证。每当您写入文件时,都应验证{{1} },因为它会捕获流错误以及您上次写入的错误,这些错误可能直到关闭才报告)

该代码遵循您的方法,仅在几个地方进行了改进。看一下:

save_patients()

注意:您在fclose中重复分配给#include <stdio.h> #include <string.h> #include <stdlib.h> #define DB_NAME "database" #define MAXRX 100 /* if you need constants, #define on (or more) */ #define MAXNM 20 /* (don't use "magic numbers" in your code ) */ typedef struct { int day, month, year; } dob_t; typedef struct { int medicine_id; char medicine_name[MAXRX]; } medicine_t; typedef struct patient { int patient_id; dob_t date_db; char patient_name[MAXNM]; medicine_t patient_med; struct patient* nextp; } patient_t; void empty_stdin (void) { int c = getchar(); while (c != '\n' && c != EOF) c = getchar(); } void print_menu (void); patient_t *add_patients (patient_t **patient_headp, int *num_patients); void view_patients (patient_t *patient_headp); int save_patients (patient_t *patient_headp); int read_patients (patient_t **patient_headp, int *num_patients); int main (void) { patient_t *patient_headp = NULL; int option_picked = 0, num_patients = 0; while(option_picked != 5) { print_menu (); if (scanf("%d", &option_picked) != 1) { /* VALIDATE EVERY USER INPUT */ fputs ("\n error: invalid input.\n", stderr); empty_stdin(); continue; } if (option_picked == 1) add_patients (&patient_headp, &num_patients); else if (option_picked == 2) view_patients (patient_headp); else if (option_picked == 3) save_patients (patient_headp); else if (option_picked == 4) read_patients (&patient_headp, &num_patients); } return 0; } void print_menu (void) { printf ("\n" "1. add a patient\n" "2. display all patients\n" "3. save the patients to the database file\n" "4. load the patients from the database file\n" "5. exit the program\n\n" "Enter choice (number between 1-5)> "); } patient_t *add_patients (patient_t **patient_headp, int *num_patients) { patient_t *patient_currentp = *patient_headp, *temp = NULL; char choice = 0; do { temp = malloc (sizeof *temp); /* allocate */ if (temp == NULL){ /* validate */ perror ("add_patients-malloc"); return NULL; } temp->nextp = NULL; /* initialize */ printf ("Enter Patient ID: "); if (scanf ("%d", &temp->patient_id) != 1) goto error_add_pt; printf ("Enter Patient DOB(DD MM YY): "); if (scanf ("%d %d %d", &temp->date_db.day, &temp->date_db.month, &temp->date_db.year) != 3) goto error_add_pt; printf ("Enter Patient Name: "); if (scanf ("%s", temp->patient_name) != 1) goto error_add_pt; printf ("Enter Patient Medicine Prescription: "); if (scanf ("%s", temp->patient_med.medicine_name) != 1) goto error_add_pt; printf ("Enter Patient Medicine Prescription ID: "); if (scanf ("%d", &temp->patient_med.medicine_id) != 1) goto error_add_pt; if (*patient_headp == NULL){ *patient_headp = patient_currentp = temp; } else { while (patient_currentp->nextp != NULL){ patient_currentp = patient_currentp->nextp; } patient_currentp->nextp = temp; } (*num_patients)++; printf ("Add more patients? (Y/N) "); if (scanf (" %c", &choice) < 1) { fputs (" user canceled input.\n", stderr); break; } } while (choice == 'Y' || choice == 'y'); return temp; /* return pointer to most recent node added */ error_add_pt:; fputs ("error: invalid input\n", stderr); empty_stdin(); free (temp); return NULL; } void view_patients (patient_t *patient_headp) { patient_t *patient_currentp = patient_headp; while (patient_currentp != NULL) { printf ("%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id, patient_currentp->date_db.day, patient_currentp->date_db.month, patient_currentp->date_db.year, patient_currentp->patient_name, patient_currentp->patient_med.medicine_name, patient_currentp->patient_med.medicine_id); patient_currentp = patient_currentp->nextp; } } int save_patients (patient_t *patient_headp) { patient_t *patient_currentp = patient_headp; FILE *output = fopen(DB_NAME, "a"); if (output == NULL) { /* validate file open to append */ fprintf (stderr, "error: file open failed '%s'\n", DB_NAME); return 0; } while(patient_currentp != NULL) { fprintf (output, "%05d %02d/%02d/%02d %s %s %d\n", patient_currentp->patient_id, patient_currentp->date_db.day, patient_currentp->date_db.month, patient_currentp->date_db.year, patient_currentp->patient_name, patient_currentp->patient_med.medicine_name, patient_currentp->patient_med.medicine_id); patient_currentp = patient_currentp->nextp; } if (fclose (output) == EOF) { fputs ("error: stream error on fclose.\n", stderr); return 0; } return 1; } int read_patients (patient_t **patient_headp, int *num_patients) { patient_t tmp = {0}, *patient_currentp = *patient_headp; FILE *input = fopen(DB_NAME, "r"); if (input == NULL){ /* validate file open for reading */ fprintf (stderr, "error: file open failed '%s'\n", DB_NAME); return 0; } while (patient_currentp && patient_currentp->nextp != NULL) patient_currentp = patient_currentp->nextp; while (fscanf (input, "%05d %02d/%02d/%02d %19s %99s %d", &tmp.patient_id, &tmp.date_db.day, &tmp.date_db.month, &tmp.date_db.year, tmp.patient_name, tmp.patient_med.medicine_name, &tmp.patient_med.medicine_id) == 7) { patient_t *node = malloc (sizeof *node); if (node == NULL) { perror ("read_patients-malloc"); return 0; } node->nextp = NULL; *node = tmp; if (!patient_currentp) *patient_headp = patient_currentp = node; else { patient_currentp->nextp = node; patient_currentp = patient_currentp->nextp; } (*num_patients)++; printf ("%05d %02d/%02d/%02d %s %s %d\n", node->patient_id, node->date_db.day, node->date_db.month, node->date_db.year, node->patient_name, node->patient_med.medicine_name, node->patient_med.medicine_id); } fclose (input); return 1; } 的原因是内存泄漏并覆盖了您先前分配给列表的指针值。这就是为什么要额外添加{使用{1}}变量)

使用/输出示例-输入数据

patient_currentp

使用/输出示例-从文件读取

read_patients()

再次,仔细研究,了解为什么进行了更改,并询问是否还有其他问题。

答案 1 :(得分:0)

add_patients中,发生以下情况

创建节点

/* Create a temporary node */
temp = (patient_t*) malloc(sizeof(patient_t));

使用任何数据填充节点

/* Copy Data to temporary Node */
printf("Enter Patient ID: ");
..
..
temp->nextp = NULL;

链表更改

if(patient_headp == NULL){
    patient_headp = temp;
}
else{
    patient_currentp = patient_headp;
    while(patient_currentp->nextp != NULL){
        patient_currentp = patient_currentp->nextp;
    }
    patient_currentp->nextp = temp;
}

此时temp仍指向分配的内存,linked list仍指向该内存

然后是

free(temp);

产生segfault。因此删除free