我一直在尝试将一个字符串输入链接列表,直到按下Enter键为止。我想将每个字符放入单独的节点。
这是我的代码:
charNode* readString(){
char c;
charNode *head, *cur, *last;
head = NULL;
while(1){
scanf("%c", &c);
if(c == '\n'){
break;
}else{
if(head ==NULL){
head = malloc(sizeof(struct charNode));
head->c = c;
last = head;
last->next = NULL;
}else{
cur = malloc(sizeof(struct charNode));
cur->c = c;
cur->next = NULL;
last->next = cur;
}
}
}
return head;
}
当我在执行过程中按Enter键时,scanf函数似乎根本无法检测到它。
关于您的代码,有几点注意事项。首先,如注释中所示,您的读取循环应基于读取功能本身的返回。如果要使用scanf
,则可以执行以下操作:
while (scanf("%c", &c) == 1 && c != '\n')
尽管从stdin
中读取单个字符,但我建议例如单独执行此功能的函数>
while ((c = getchar()) != '\n' && c != EOF)
下一步,在
readString()
函数中,您无法将last
指针更新为由@JohnnyMopp指示的最后添加的节点。您需要将last = cur;
添加为else
语句下的最后一个表达式。
此外,通过在head
函数中声明tail
和readString()
并仅返回head
,您会丢失tail
指针。最好声明一个简单的wrapper
head
和tail
指针(以及您喜欢的任何其他列表统计信息),然后返回一个指向该包装器结构的指针,以保留readString()
中的所有列表信息]。例如:/** linked list node */ typedef struct node_t { char data; struct node_t *next; } node_t; /** linked list */ typedef struct { node_t *head, *tail; } list_t;
然后通过分配并返回指向
list_t
类型的指针,可以保留head
和tail
节点信息。编写readString()
函数时,将FILE*
指针作为参数传递可能会有所帮助,以便您可以从任何喜欢的打开文件流中读取。如果要从stdin
中读取,只需将stdin
作为开放流传递即可。它为您的字符串源增加了很大的灵活性,同时几乎没有增加函数的复杂性。您的readString()
函数可能是:
list_t *readstr (FILE *fp) { if (!fp) /* validate stream not NULL */ return NULL; int c; /* int to read (must be int for EOF) */ list_t *l = malloc (sizeof *l); /* allocate for list */ if (!l) { /* validate list allocation */ perror ("malloc-l"); return NULL; } l->head = l->tail = NULL; /* initialize list head/tail ptrs NULL */ if (fp == stdin) /* if reading from stdin */ fputs ("enter string: ", stdout); /* prompt for string */ while ((c = fgetc(fp)) != '\n' && c != EOF) /* read each character */ if (!add (l, c)) { /* add node, validate */ del_list (l); /* on add failure, free all memory */ l = NULL; /* set pointer NULL */ break; } return l; /* return pointer to list on success, NULL otherwise */ }
以这种方式进行处理使列表存储器的实际填充,使用和释放变得非常简单。简短的
main()
将减少为:
int main (void) { list_t *l = NULL; /* pointer to list */ if ((l = readstr (stdin))) { /* read string into list/validate */ prn (l); /* print all nodes */ del_list (l); /* free all allocated memory */ } }
从
char
读取的stdin
的链表的简短实现如下:
通过使用#include <stdio.h> #include <stdlib.h> /** linked list node */ typedef struct node_t { char data; struct node_t *next; } node_t; /** linked list */ typedef struct { node_t *head, *tail; } list_t; /** add node at end of list, update tail to end */ node_t *add (list_t *l, char c) { node_t *node = malloc (sizeof *node); /* allocate node */ if (!node) { /* validate allocation */ perror ("malloc-node"); return NULL; } node->data = c; /* initialize members values */ node->next = NULL; if (!l->head) /* if 1st node, node is head/tail */ l->head = l->tail = node; else { /* otherwise */ l->tail->next = node; /* add at end, update tail pointer */ l->tail = node; } return node; /* return new node */ } /** print all nodes in list */ void prn (list_t *l) { if (!l->head) { puts ("list-empty"); return; } for (node_t *n = l->head; n; n = n->next) printf (" %c", n->data); putchar ('\n'); } /** delete all nodes in list */ void del_nodes (list_t *l) { node_t *n = l->head; while (n) { node_t *victim = n; n = n->next; free (victim); } } /** delete list and all nodes in list */ void del_list (list_t *l) { del_nodes (l); free (l); } list_t *readstr (FILE *fp) { if (!fp) /* validate stream not NULL */ return NULL; int c; /* int to read (must be int for EOF) */ list_t *l = malloc (sizeof *l); /* allocate for list */ if (!l) { /* validate list allocation */ perror ("malloc-l"); return NULL; } l->head = l->tail = NULL; /* initialize list head/tail ptrs NULL */ if (fp == stdin) /* if reading from stdin */ fputs ("enter string: ", stdout); /* prompt for string */ while ((c = fgetc(fp)) != '\n' && c != EOF) /* read each character */ if (!add (l, c)) { /* add node, validate */ del_list (l); /* on add failure, free all memory */ l = NULL; /* set pointer NULL */ break; } return l; /* return pointer to list on success, NULL otherwise */ } int main (void) { list_t *l = NULL; /* pointer to list */ if ((l = readstr (stdin))) { /* read string into list/validate */ prn (l); /* print all nodes */ del_list (l); /* free all allocated memory */ } }
(<< [note:
list_t
包装器,您可以声明并填充任意数量的列表,并且将为每个列表保留head
和tail
指针)此外,单独的del_nodes()
和del_list()
函数使您可以将list_t
结构与自动存储持续时间
使用(例如list_t l = { NULL, NULL };
),然后仅使用free()
个已分配的节点而无需调用free()
]上的列表。示例使用/输出
$ ./bin/llchargetchar
enter string: my dog has fleas
m y d o g h a s f l e a s
在您编写的任何可动态分配内存的代码中,对于任何已分配的内存块,您都有2个内存使用/错误检查
职责
:(1)始终保留指向起始地址的指针,因此,( 2)不再需要它时,可以将其freed。必须使用内存错误检查程序,以确保您不尝试访问内存或不要在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了已分配的所有内存。对于Linux valgrind
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
$ valgrind ./bin/llchargetchar
==25923== Memcheck, a memory error detector
==25923== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==25923== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==25923== Command: ./bin/llchargetchar
==25923==
enter string: my dog has fleas
m y d o g h a s f l e a s
==25923==
==25923== HEAP SUMMARY:
==25923== in use at exit: 0 bytes in 0 blocks
==25923== total heap usage: 19 allocs, 19 frees, 2,320 bytes allocated
==25923==
==25923== All heap blocks were freed -- no leaks are possible
==25923==
==25923== For counts of detected and suppressed errors, rerun with: -v
==25923== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细检查,如果还有其他问题,请告诉我。