C 是否有办法读取字符并将其存储在链表中,直到按下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 =

我一直在尝试将一串字符读入链表,直到按下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
指针,如所示。您需要添加
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;
然后,通过分配并返回指向type
list\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;
然后,通过分配并返回指向type
list\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
是正常的选择。每个平台都有类似的内存检查器。他们都很简单