我一直在尝试将字符串读取到链表中,直到按下回车键。我想将每个字符放置到单独的节点中。
这是我的代码:
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;
}
当我在执行过程中按下回车键时,scanf函数似乎根本没有检测到它。
关于您的代码有几个注意事项。首先,如注释所示,您应该将读取循环建立在读取函数本身的返回基础上。如果你想使用scanf
,那么你可以做:
while (scanf("%c", &c) == 1 && c != 'n')
虽然如果从stdin
中读取单个字符,我建议使用单独执行该操作的函数,例如
while ((c = getchar()) != 'n' && c != EOF)
接下来,在readString()
函数中,您无法将last
指针更新为@JohnnyMopp所指示的添加的最后一个节点。您需要添加last = cur;
作为else
语句下的最后一个表达式。
此外,通过在readString()
函数中声明head
和tail
并只返回head
,您将丢失tail
指针。最好声明一个包含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 */
}
}
从stdin
读取char
的链表的简短实现可以如下:
#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 */
}
}
(注意:通过使用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(当不再需要时,它可以被释放。
您必须使用内存错误检查程序来确保您不会试图访问内存或在分配的块的边界之外写入,尝试读取或基于未初始化的值进行条件跳转,最后确认您释放了所有分配的内存。
对于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)
请始终确认您已经释放了分配的所有内存,并且没有内存错误。
仔细看看,如果你还有问题,请告诉我。