为什么我的教授的链接列表向后打印?

Why is my Professor's Linked List Printing Backwards?

我有一个系统编程课程的项目。我正在建立我教授的代码。 (请不要介意她缺少标签等 - 我会尽力清理它。)

有谁知道为什么她的链表代码打印反了?

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
    
struct Node {
    char name[15];
    char title[15];
    int year;
    struct Node *next;
    struct Node *prev;
};
    
typedef struct Node *Box;
    
Box print_list(Box pointer);
Box insert_node(FILE *inputp);
    
int main() {
    Box head = NULL, temp;
    FILE *inputp, *outputp;
    int i;
    
    inputp = fopen("input.txt", "r");
    
    outputp = fopen("output.txt", "w");
    
    head = insert_node(inputp);
        
    for (i = 0; i < 4; i++) {
        temp = insert_node(inputp);
        temp->next = head;
        head = temp;
    }
        
    print_list(head);
    return 0;
}
    
Box print_list(Box pointer) {
    
    Box here = pointer;
    
    while (here != NULL) {
    
        printf("%s, %s, %d \n", here->name, here->title, here->year);
        here = here->next;  
    }
    
    return pointer;
}
    
Box insert_node(FILE *inputp) {
    
    Box temp = NULL;
    
    temp = (Box)malloc(sizeof(struct Node));
    
    fscanf(inputp, "%s", &temp->name);
    fscanf(inputp, "%s", &temp->title);
    fscanf(inputp, " %d", &temp->year);
    
    temp->next = NULL;
    temp->prev = NULL;
        
    return temp;
}

该程序的目的是读取歌曲的 .txt 文件“播放列表”并从中创建一个链接列表。输入是:

Rachmaninov Concerto_No_2 1999
Mozart Symphony_No_41 2000
Vivaldi The_Seasons 2003 
Beethoven Symphony_No_5 1994
Bach Toccatas 2005

当程序输出:

Bach, Toccatas, 2005 
Beethoven, Symphony_No_5, 1994 
Vivaldi, The_Seasons, 2003 
Mozart, Symphony_No_41, 2000 
Rachmaninov, Concerto_No_2, 1999

(我也不知道她为什么在代码中包含一个输出文件,所有输出都在控制台,而不是存储在文件中。忽略它。)

列表以相反的顺序打印,因为您在列表的开头插入了每个新节点。您应该使用 tail 指针来跟踪列表的末尾。

另请注意以下备注:

  • nextprev 链接都应该更新。

  • typedef struct Node *Box; 那样将指针隐藏在 typedef 后面被认为是不好的做法,因为它令人困惑且容易出错。

  • insert_node 是一个容易混淆的函数名称,它只是从文件数据中分配一个新节点。

  • insert_node 应该测试 fscanf() 是否成功读取数据

  • 如果作曲家的名字超过 14 个字节,
  • fscanf(inputp, "%s", &temp->name); 有未定义的行为。这同样适用于title。在空终止符之前要存储到目标数组中的最大字符数应指定为 %14s,并且这些数组应定义为更大的长度。

  • main 应该检查节点是否从文件数据成功分配和初始化。只要可以从文件中读取节点,就应该迭代而不是硬编码节点数。

这是修改后的版本:

#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
    
struct Node {
    char name[40];
    char title[40];
    int year;
    struct Node *next;
    struct Node *prev;
};
    
void print_list(const Node *pointer);
Node *read_node(FILE *inputp);
    
int main() {
    Node *head = NULL;
    Node *tail = NULL;
    Node *node;
    FILE *inputp, *outputp;
    int i;

    inputp = fopen("input.txt", "r");
    if (!inputp) {
       fprintf(stderr, "cannot open input.txt: %s\n", strerror(errno));
       return 1;
    }
    outputp = fopen("output.txt", "w");
    if (!outputp) {
       fprintf(stderr, "cannot open output.txt: %s\n", strerror(errno));
       return 1;
    }
    
    while ((node = read_node(inputp)) != NULL) {
        if (!head) {
            head = tail = node;
        } else {
            node->prev = tail;
            tail = tail->next = node;
        }
    }
        
    print_list(head);

    // should free node list

    return 0;
}
    
void print_list(const Node *pointer) {
    while (pointer != NULL) {
        printf("%s, %s, %d\n", pointer->name, pointer->title, pointer->year);
        pointer = pointer->next;  
    }
}
    
Node *read_node(FILE *inputp) {
    Node *temp = malloc(sizeof(*temp));
    
    if (temp != NULL
    &&  fscanf(inputp, "%39s%39s%d", &temp->name, &temp->title, &temp->year) == 3) {
        temp->next = NULL;
        temp->prev = NULL;
        return temp;
    } else {
        free(temp);
        return NULL;
    }
}