您的代码中有许多问题需要纠正(有些问题比其他问题多)。
您的第一个问题可以通过阅读Why is while ( !feof (file) ) always wrong? 来解决。 (简短的回答是EOF 不是在fscanf 读取的最终成功 上生成,因此在读取最后一个“名称”后,您检查!feof(file) 测试TRUE,循环继续, fscanf 以 EOF 失败,您分配然后尝试从 buf 中获取不确定值的 strcpy -- 调用 Undefined Behavior)
虽然在这种情况下,在阅读没有空格的单词时,您可以使用fscanf -- 但不要。早点养成好习惯。从文件的每一行获取输入时,使用面向行的 输入函数(例如fgets 或POSIX getline)来确保输入缓冲区中剩余的内容不依赖于scanf 使用了转换说明符。
例如,您可以:
#define MAXN 50 /* if you need a constant, #define one (or more) */
typedef struct node {
char name[MAXN];
struct node *next;
}
node;
...
/* 读取文件内容并写入链表 */
while (fgets (buf, MAXN, file)) {
/* valdiating the last char is '\n' or length < MAXN - 1 omitted */
/* allocate new node */
node *head = malloc (sizeof(node));
if (head == NULL) { /* validate allocation */
perror ("malloc-node"); /* issue error message */
return 1; /* don't return negative values to the shell */
}
/* trim '\n' from end of buf */
buf[strcspn(buf, "\n")] = 0; /* overwrite with nul-character */
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
(注意: 不要将 负 值返回到您的 shell,并且不要在 -> 运算符之后包含 空格引用结构的成员)
此时,您已成功将“名称”读入buf,为您的新节点分配,验证分配成功,将buf中包含的尾部'\n'修剪为面向行的输入函数,将修剪后的“名称”复制到head->name,并将head->next初始化为NULL。但是,你有一个问题。你所有的名字都以reverse-order的形式存储在你的列表中(有时可能是你想要的)。但这通常不是所需的行为。通过简单地声明一个更新为指向最后一个节点的附加指针,您可以按顺序插入到列表中,而无需迭代查找最后一个节点。
例如,简单地声明一个 last 指针并检查两种情况 (1) last=NULL 指示循环为空,并使 first 和 last 指向 first 节点允许您 (2)在末尾添加所有其他节点,例如:
node *first = NULL, *last = NULL; /* use last for in-order chaining */
...
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
/* in-order add at end of list */
if (!last)
first = last = head;
else {
last->next = head;
last = head;
}
}
@CS Pei 正确解决了您的“无限循环”,您只是忘记在输出循环中将当前指针前进到 ptr->next。
您还未能free 分配的内存。是的,它在程序退出时被释放,但养成跟踪您分配的所有内存然后在不再需要时释放该内存的习惯。 (随着代码的增长,这成为防止内存泄漏的重要技能)。例如,在你的输出循环中,你可以这样做:
node *ptr = first; /* declare/set pointer to first */
while (ptr != NULL) { /* loop while ptr */
node *victim = ptr; /* declare/set pointer to victim */
printf ("%s\n", ptr->name); /* output name */
ptr = ptr->next; /* advance pointer to next */
free (victim); /* free victim */
}
总而言之,您可以执行类似于以下的操作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 50 /* if you need a constant, #define one (or more) */
typedef struct node {
char name[MAXN];
struct node *next;
}
node;
int main (int argc, char **argv) {
FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
if (file == NULL)
return 1;
char buf[MAXN];
node *first = NULL, *last = NULL; /* use last for in-order chaining */
/* read the contents of the file and write to linked list */
while (fgets (buf, MAXN, file)) {
/* valdiating the last char is '\n' or length < MAXN - 1 omitted */
/* allocate new node */
node *head = malloc (sizeof(node));
if (head == NULL) { /* validate allocation */
perror ("malloc-node"); /* issue error message */
return 1; /* don't return negative values to the shell */
}
/* trim '\n' from end of buf */
buf[strcspn(buf, "\n")] = 0; /* overwrite with nul-character */
/* initialize node values */
strcpy (head->name, buf);
head->next = NULL;
/* in-order add at end of list */
if (!last)
first = last = head;
else {
last->next = head;
last = head;
}
}
if (file != stdin) /* close file if not stdin */
fclose(file);
node *ptr = first; /* declare/set pointer to first */
while (ptr != NULL) { /* loop while ptr */
node *victim = ptr; /* declare/set pointer to victim */
printf ("%s\n", ptr->name); /* output name */
ptr = ptr->next; /* advance pointer to next */
free (victim); /* free victim */
}
return 0;
}
使用/输出示例
使用您的输入文件,您将获得以下输出:
$ ./bin/ll_chain_last <dat/llnames.txt
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/ll_chain_last <dat/llnames.txt
==24202== Memcheck, a memory error detector
==24202== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24202== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==24202== Command: ./bin/ll_chain_last
==24202==
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
==24202==
==24202== HEAP SUMMARY:
==24202== in use at exit: 0 bytes in 0 blocks
==24202== total heap usage: 9 allocs, 9 frees, 576 bytes allocated
==24202==
==24202== All heap blocks were freed -- no leaks are possible
==24202==
==24202== For counts of detected and suppressed errors, rerun with: -v
==24202== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。