C 是否有办法读取字符并将其存储在链表中,直到按下Enter键?
我一直在尝试将一串字符读入链表,直到按下enter键。我想将每个角色放置到单独的节点中 这是我的代码:C 是否有办法读取字符并将其存储在链表中,直到按下Enter键?,c,scanf,C,Scanf,我一直在尝试将一串字符读入链表,直到按下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 =
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
指针,如所示。您需要添加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;
然后,通过分配并返回指向typelist\u t
的指针,可以保留头
和尾
节点信息。编写readString()
函数时,将文件*
指针作为参数传递可能会有所帮助,这样您就可以从任意打开的文件流中读取。如果您想读取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 */
}
}
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于所分配的任何内存块,您有两个责任:(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)
始终确认已释放所有已分配的内存,并且没有内存错误
仔细检查一下,如果您还有其他问题,请告诉我。关于您的代码,这里有一些注释。首先,如注释中所示,您应该将读取循环建立在读取函数本身返回的基础上。如果要使用scanf
,则可以执行以下操作:
while (scanf("%c", &c) == 1 && c != '\n')
虽然如果从stdin
读取单个字符,我建议使用单独执行此操作的函数,例如
while ((c = getchar()) != '\n' && c != EOF)
接下来,在readString()
函数中,无法更新指向所添加的最后一个节点的last
指针,如所示。您需要添加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;
然后,通过分配并返回指向typelist\u t
的指针,可以保留头
和尾
节点信息。编写readString()
函数时,将文件*
指针作为参数传递可能会有所帮助,这样您就可以从任意打开的文件流中读取。如果您想读取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 */
}
}
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于所分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要它时可以释放它
必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或基于条件跳转,最后确认释放所有已分配的内存
对于Linux,valgrind
是正常的选择。每个平台都有类似的内存检查器。他们都很简单