我是 C 编程的新手。
我想将文件中的内容添加到链接列表。
文件中的内容是名称,每个名称都应该有自己的节点。
但是,当运行代码时,我得到一个无限循环。
我试图解决,但我无法深入了解它。
我有一种感觉是 fscanf 导致了这个问题。
先感谢您。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
char name[50];
struct node * next;
}
node;
int main() {
FILE * file = fopen("content.txt", "r");
if (file == NULL)
return 1;
char buf[50];
node * first = NULL;
//read the contents of the file and write to linked list
while (!feof(file)) {
fscanf(file, "%s", buf);
// try to instantiate node
node * head = malloc(sizeof(node));
if (head == NULL) {
return -1;
}
head-> next = NULL;
// add new word to linked list
strcpy(head-> name, buf);
//move node to start of linked list
head-> next = first;
first = head;
}
fclose(file);
node * ptr = first;
while (ptr != NULL) {
printf("%s\n", ptr-> name);
}
return 0;
}
这就是输入文件的样子。
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
最佳答案
您的代码中有许多问题需要纠正(比其他问题更多)。
您的第一个问题可以通过阅读 Why is while ( !feof (file) ) always wrong? 来解决. (简短的答案是 EOF
不是在最终的 上生成 由 fscanf
读取,因此在读取最后一个“名称”后,您检查 !feof(file)
测试 TRUE
,循环继续, fscanf
失败并显示 EOF
,您分配然后尝试从 strcpy
获取不确定值的 buf
—— 调用未定义行为)
在这种情况下,如果阅读没有空格的单词,您可以使用 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
,分配给您的新节点,已验证 分配成功,修剪尾随'\n'
包含在 buf
中通过面向行的输入功能,将修剪后的“名称”复制到head->name
并初始化head->next
至NULL
.但是,你有一个问题。您的所有姓名都以相反的顺序存储在您的列表中(有时可能是您想要的)。但这通常不是所需的行为。通过简单地声明一个更新为指向最后一个节点的附加指针,您可以按顺序插入到列表中,而无需迭代查找最后一个节点。例如,简单地声明一个
last
指针和检查两种情况(一)last=NULL
指示循环为空并制作 first
和 last
指向first
node 允许您(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
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,(2)它可以在没有时被释放更需要。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配 block 的范围,尝试读取或基于未初始化的值进行条件跳转,最后确认释放所有分配的内存。
对于 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)
始终确认您已释放所有已分配的内存并且没有内存错误。
如果您还有其他问题,请仔细查看并告诉我。
关于c - 将文件中的内容添加到链表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53824683/