gpt4 book ai didi

c - 尝试从文本文件中读取单词并添加到链接列表,程序崩溃,我不知道为什么

转载 作者:行者123 更新时间:2023-11-30 17:03:36 26 4
gpt4 key购买 nike

我的程序应该从文本文件中读取每个单词,然后将其添加到链接列表中,但在我输入用户输入后它崩溃了。我不知道为什么。我尝试了很多不同的功能和方法,但它仍然崩溃。我搞不清楚了。

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


struct node
{
char *data;
struct node *next;
};


void insertNode(struct node**, char *);
void printList(struct node*);


int main()
{
struct node *head = NULL;
FILE *fptr;
char file_name[20];
char str[1000];
int numOfChar;


printf("Enter the name of the file: ");
scanf("%s",file_name);


printf("Enter the number of characters per line: ");
scanf("%d",&numOfChar);


fptr=fopen(file_name,"r");

while(fscanf(fptr, "%s ", str) != EOF)
{
insertNode(&head, str);
}

fclose(fptr);
printList(head);


return 0;
}


void insertNode(struct node** nodeHead, char *data)
{
struct node* new_node = malloc(sizeof *new_node);
new_node->data = strdup(data);
new_node->next = NULL;

while (*nodeHead)
nodeHead = &(*nodeHead)->next;
*nodeHead = new_node;

}

void printList(struct node* node)
{
while(node != NULL)
{
printf(" %s ", node->data);
node = node->next;
}
}

最佳答案

无论您使用哪种语言进行编码,如果您没有验证您的输入并且函数调用返回,您就无法确信您的代码正在正确执行任何操作,更重要的是,您将不知道在什么时候你的代码开始失败。这是根本性的。我知道您正在学习,但要从验证所有输入和所有函数调用返回的好习惯开始。您作为程序员的生活将会变得更加轻松。

实现简单的验证将告诉您代码在哪里失败以及在什么时候开始失败。例如,每次您接受输入时,请验证您确实得到了您所期望的内容。随着scanf函数系列,这意味着至少要验证已成功进行的转换次数。如果您不清楚函数返回什么,或者如何衡量成功/失败,请查看该函数的手册页。该声明准确地告诉您期望返回的类型,然后手册页的正文准确地告诉您成功/失败的含义。

    printf ("Enter the name of the file: ");
if (scanf("%[^\n]%*c", file_name) != 1) { /* validate conversion */
fprintf (stderr, "error: scanf filename conversion failed.\n");
return 1;
}

printf ("Enter the number of characters per line: ");
if (scanf ("%d%*c",&numOfChar) != 1) { /* validate conversion */
fprintf (stderr, "error: scanf numOfChar conversion failed.\n");
return 1;
}

if (!(fptr = fopen (file_name,"r"))) { /* validate file open */
fprintf (stderr, "error: file open failed for '%s'.\n", file_name);
return 1;
}

(侧节点:C 的样式规范,避免使用 caMelCase 变量名称,首选全部小写。参见例如 NASA - C Style Guide, 1994 )

在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,< strong>(2)当不再需要时可以将其释放。

您必须使用内存错误检查程序来确保您正确使用分配的内存,并且在不再需要时将其全部释放。有许多微妙的方法可以滥用新的内存块。对于 Linux valgrind是正常的选择。 (使用起来非常简单,只需valgrind ./yourprogram)

要释放列表,您需要释放 strdup 分配的所有内存。以及直接使用 malloc 分配的内存。对于列表,您需要创建第二个指针来保存要释放的 block 的地址,以便您可以分配 node = node->next 删除节点之前。一个简短的例子是:

void freelist (struct node *node)
{
while (node) {
struct node *victim = node;
node = node->next;
free (victim->data);
free (victim);
}
}

将所有部分放在一起,进行最少的验证并正确使用 free可能看起来像:

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

enum { MAXF = 20, MAXC = 1000 };

struct node {
char *data;
struct node *next;
};

void insertnode (struct node**, char *);
void printlist (struct node*);
void freelist (struct node *node);

int main (void) {

struct node *head = NULL;
FILE *fptr;
char file_name[MAXF];
char str[MAXC];
int numOfChar = 0; /* note: does nothing currently */

printf ("Enter the name of the file: ");
if (scanf("%[^\n]%*c", file_name) != 1) { /* validate conversion */
fprintf (stderr, "error: scanf filename conversion failed.\n");
return 1;
}

printf ("Enter the number of characters per line: ");
if (scanf ("%d%*c",&numOfChar) != 1) { /* validate conversion */
fprintf (stderr, "error: scanf numOfChar conversion failed.\n");
return 1;
}

if (!(fptr = fopen (file_name,"r"))) { /* validate file open */
fprintf (stderr, "error: file open failed for '%s'.\n", file_name);
return 1;
}

while (fscanf (fptr, "%s ", str) != EOF)
insertnode (&head, str);

fclose (fptr);
printlist (head);
freelist (head);

return 0;
}

void insertnode (struct node **nodeHead, char *data)
{
struct node *new_node = malloc (sizeof *new_node);
if (!new_node) {
fprintf (stderr, "error: virtual memory exhausted - new_node.\n");
exit (EXIT_FAILURE);
}
new_node->data = strdup(data);
new_node->next = NULL;

while (*nodeHead)
nodeHead = &(*nodeHead)->next;
*nodeHead = new_node;
}

void printlist (struct node *node)
{
while (node) {
printf(" %s ", node->data);
node = node->next;
}
putchar ('\n');
}

void freelist (struct node *node)
{
while (node) {
struct node *victim = node;
node = node->next;
free (victim->data);
free (victim);
}
}

仔细检查一下,如果您还有其他问题,请告诉我。

insertnode 的第二种方法

由于您遇到 insertnode 的问题,还有另一种方法可以迭代当前节点(实际上还有更多)。尝试以下操作:

void insertnode (struct node **nodeHead, char *data)
{
if (!data || !*data) {
fprintf (stderr, "error: invalid 'data' passed to insertnode '%s'.\n",
data);
exit (EXIT_FAILURE);
}

struct node *new_node = malloc (sizeof *new_node);
if (!new_node) {
fprintf (stderr, "error: virtual memory exhausted - new_node.\n");
exit (EXIT_FAILURE);
}
new_node->data = strdup(data);
new_node->next = NULL;

if (!*nodeHead) {
*nodeHead = new_node;
}
else {
while ((*nodeHead)->next)
nodeHead = &((*nodeHead)->next);
(*nodeHead)->next = new_node;
}
}

使用单独的指针进行迭代

void insertnode (struct node **nodeHead, char *data)
{
if (!data || !*data) {
fprintf (stderr, "error: invalid 'data' passed to insertnode '%s'.\n",
data);
exit (EXIT_FAILURE);
}

struct node *new_node = malloc (sizeof *new_node);
if (!new_node) {
fprintf (stderr, "error: virtual memory exhausted - new_node.\n");
exit (EXIT_FAILURE);
}
new_node->data = strdup(data);
new_node->next = NULL;

if (!*nodeHead)
*nodeHead = new_node;
else {
struct node *iter = *nodeHead;
for (; iter->next; iter = iter->next) {}
iter->next = new_node;
}
}
<小时/>

回答您的第二个问题与打印

虽然我喜欢fgets接近你的列表,比 scanf 好得多,我比现在更喜欢你的第一个列表布局。由于评论中给出的原因,您在这里的解析实现是一场灾难。 strtok (或 strsep )是将字符串分解为单词的正确解析函数(或者您可以使用指针来完成它 - 但它需要的比您拥有的要多得多。查看 strtok 的手册页。这是一个使用的示例它进行分词:

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

typedef struct list {
char *string;
struct list *next;
} LIST;

void freelist (struct list *node);

int main (void) {

FILE *fp;
char line[128];
char file_name[20];
LIST *current, *head;
char *p;
char *delim = " \t\r\n";

head = current = NULL;
printf ("Enter the name of the file: ");
scanf("%s",file_name);

fp = fopen(file_name, "r");

while(fgets(line, sizeof(line), fp))
{
for (p = strtok (line, delim); p; p = strtok (NULL, delim))
{
LIST *node = malloc (sizeof(LIST));
if (!(node->string = strdup (p))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
node->next = NULL;

if (!head)
current = head = node;
else {
current->next = node;
current = current->next;
}
}
}

fclose(fp);

for (current = head; current; current = current->next) {
printf(" %s", current->string);
}
putchar ('\n');
freelist (head);

return 0;
}

void freelist (struct list *node)
{
while (node) {
struct list *victim = node;
node = node->next;
free (victim->string);
free (victim);
}
}

尝试一下——希望没有问题——我在键盘上睡着了。

关于c - 尝试从文本文件中读取单词并添加到链接列表,程序崩溃,我不知道为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36108661/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com