C:用于构建char *链接列表的fgets用法

时间:2017-09-15 15:32:24

标签: c linked-list fgets

我是否错误地使用fgets()

我正在尝试构建一个字符串链接列表(char *),将每个新行添加到LL的末尾。我正在从文件中读取这些行,但由于某种原因,每个行都被正在处理的当前行覆盖,只有在fgets()循环内使用while时,但是add函数似乎正在接收每个行正确排队。

如果我在main()中单独添加行,则没有问题。

以下是输入文件示例:

input.txt中:

This life, which had been the
tomb of his virtue and of his
honour, is but a walking
shadow; a poor player, that
struts and frets his hour upon
the stage, and then is heard
no more: it is a tale told by an
idiot, full of sound and fury,
signifying nothing.
    --William Shakespeare

代码:

#include <stdio.h> //printf, fopen
#include <stdlib.h> //exit, EXIT_FAILURE
#include <string.h> //strlen

struct node {
    char *line;
    struct node *next;
};

void print(struct node *node);

void add(struct node **head, char *newLine) {
    //printf("%s", newLine);

    struct node *new_node = (struct node *)malloc(sizeof(struct node));
    struct node *curr = *head;

    new_node->line = newLine;
    new_node->next = NULL;

    if (*head == NULL) {
        *head = new_node;
    } else {
        while (curr->next != NULL) {
            curr = curr->next;
        }
        curr->next = new_node;
    }
    print(*head);
}

void print(struct node *node) {
    printf("\n");

    while (node != NULL) {
        printf("%s\n", node->line);
        node = node->next;
    }
}

int main(int argc, char *argv[16]) {
    char newLine[81];
    struct node *head = NULL;
    FILE *fp = fopen(argv[1], "r");

    if (fp == NULL) {
        printf("ERROR: file open failed");
        exit(EXIT_FAILURE);
    }

    while (fgets(newLine, 81, fp)) {
        add(&head, newLine);
    }

    add(&head, "why");
    add(&head, "does");
    add(&head, "this");
    add(&head, "work??");

    fclose(fp);

    print(head);

    return 0;
}

有人可以向我解释发生了什么事吗?我一直把头撞在墙上太久了。我已经尝试使用一些注释的打印语句,但没有成功进行调试。

3 个答案:

答案 0 :(得分:2)

您的问题出在add()方法中。 它不断向列表添加相同的缓冲区指针。 您需要将列表中的缓冲区复制到新分配的空间,即。 node-&gt;行也需要进行malloced,并将newLine复制到其中。 不要忘记malloc(strlen(newLine)+ 1)。

答案 1 :(得分:1)

您有一个缓冲区,用于存储输入。并且在添加节点时将指针传递给此单个缓冲区的第一个元素。这意味着所有节点中的字符串指针将指向同一个缓冲区。最后会包含您读取的最后一个字符串。

最简单的解决方案是使节点结构中的字符串成为一个数组,并将字符串复制到其中。

另一个解决方案是动态地为字符串分配内存(记住终止空字符)并再次将字符串复制到该内存中。

使用常量字符串文字时的区别在于每个字符串都是不同的数组。

答案 2 :(得分:1)

您必须为每一行分配内存。按照当前编码,所有节点都指向main()中的本地数组,其内容将被每次调用fgets()覆盖。

另请注意,添加到列表中的每一行都包含一个终止换行符,您可能应该在通话之前将其删除。

以下是更正后的版本:

#include <stdio.h>  // printf, fopen
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h> // strlen, strdup

struct node {
    char *line;
    struct node *next;
};

void print(struct node *node);

void add(struct node **head, char *newLine) {
    //printf("%s", newLine);

    struct node *new_node = malloc(sizeof(struct node));
    struct node *curr = *head;

    new_node->line = strdup(newLine);
    new_node->next = NULL;

    if (*head == NULL) {
        *head = new_node;
        return;
    }

    while (curr->next != NULL) {
        curr = curr->next;
    }

    curr->next = new_node;
    print(*head);
}

void print(const struct node *node) {
    printf("\n");

    while (node != NULL) {
        printf("%s\n", node->line);
        node = node->next;
    }
}

int main(int argc, char *argv[16]) {
    char newLine[81];
    struct node *head = NULL;
    FILE *fp = fopen(argv[1], "r");

    if (fp == NULL) {
        printf("ERROR: file open failed");
        exit(EXIT_FAILURE);
    }

    while (fgets(newLine, sizeof newLine, fp)) {
        newLine[strcspn(newLine, "\n")] = '\0'; // strip the newline if present
        add(&head, newLine);
    }

    add(&head, "why");
    add(&head, "does");
    add(&head, "this");
    add(&head, "work??");

    fclose(fp);

    print(head);

    return 0;
}