Есть ли способ прочитать символы и сохранить их в связанном списке, пока не будет нажата Enter? - PullRequest
0 голосов
/ 29 февраля 2020

Я пытался прочитать строку символов в связанный список, пока не будет нажата кнопка ввода. Я хочу поместить каждый символ в отдельный узел.

Это мой код:

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 не отображается чтобы обнаружить это вообще.

1 Ответ

2 голосов
/ 29 февраля 2020

Есть пара замечаний о вашем коде. Во-первых, как указано в комментариях, вы должны основывать свой read-l oop на возврате самой функции read. Если вы хотите использовать scanf, то вы можете сделать:

while (scanf("%c", &c) == 1 && c != '\n')

Хотя, если читать один символ из stdin, я бы порекомендовал функцию, которая делает это самостоятельно, например,

* 1007. *

Далее, в вашей функции readString() вы не можете обновить указатель last на последний добавленный узел, как указано @ JohnnyMopp . Вам нужно будет добавить last = cur; в качестве последнего выражения в вашем операторе else.

Кроме того, объявив head и tail в функции readString() и вернув только 1010 *, вы потеряете указатель 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 */
    }
}

( примечание: с помощью оболочки list_t вы можете объявить и заполнить столько списков, сколько вам нужно, и вы сохраните указатели head и tail для каждого списка)

Кроме того, отдельные функции del_nodes() и del_list() позволяют использовать структуру list_t с automa c длительность хранения (например, 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)

Всегда подтверждайте, что вы освободили всю выделенную память и что нет ошибок памяти.

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

...