C语言中链表实现的错误

4

我有一个用C语言编写的程序,但是我遇到了一个问题,无法理解其影响。该应用程序将一系列单词作为命令行输入进行读取。在读取输入时,它逐个将单词放入列表中,然后打印该列表。令我困惑的是,为什么在循环内添加的值不会被正确打印,而在循环外添加的值则会被正确打印。也就是说,无论用户输入的是哪些值,只有最后一个值会被打印出来。此外,它将按照输入的值的数量重复打印相同的值。两个主要嫌疑人是push和printList方法:

void push(struct List * list, char* newValue){
    assert(list != NULL);
    assert(newValue != NULL);

    Node* node = createNode();
    node->data = newValue;
    node->next = NULL;
    if(list->firstNode != NULL){
        node->next = list->firstNode;
        list->firstNode = node;
    }
    else list->firstNode = node;
}

void printList(struct List * list){
    assert(list != NULL);
    Node *node = list->firstNode;
    while(node->next != NULL){
        printf("%s ", node->data);
        node = node->next;
    }
    if(node != NULL) printf("%s ", node->data);
}

但我在那里找不到任何错误。我的做法是将有和没有 while 循环的行为进行比较:

int main(){
    struct List* list = createList();
    char s[256];
    int a;
    push(list, "he");
    push(list, "bee");
    push(list, "gee");
    while(scanf("%s", &s) == 1) push(list, s);
    printList(list);
}

我得到的输出是:

c c c gee bee he

而输入是:

a b c

所以我期望得到的是:

c b a gee bee he

我错过了什么?非常感谢您的任何建议。
附:上述使用的类型定义和方法为:
typedef struct Node {
    char* data;
    struct Node *next;
} Node;

typedef struct List {
    struct Node *firstNode;
} List;

Node *createNode(){
    Node* node = malloc(sizeof(struct Node));
    assert(node != NULL);

    node->data = "";
    node->next = NULL;
    return node;
}

List *createList(){
    List* list = malloc(sizeof(struct List));
    list->firstNode = NULL;
    assert(list != NULL);
    return list;
}

1
顺便提一下,在 createList 中,您将断言放在了错误的位置,因此它将没有任何效果:如果 list 为空,那么在到达断言之前,您将会遇到段错误。在尝试访问指针返回值之前,请始终检查 malloc 的返回值。 - Tom Karzes
2
你忽略了 C 语言中没有字符串数据类型这一事实。node->data = newValue 赋值的是一个 指针,当你调用 push(list, s); 时,每次传递的都是同一个指针。 - n. m.
在调用 push 之前,您需要为字符串分配存储空间。对于字符串常量,这不是问题,但对于由 scanf 读取的字符串,您一遍又一遍地使用自动数组 s。您可以使用 strdup 来分配存储空间并复制字符串。 - Tom Karzes
2个回答

5
while(scanf("%s", &s) == 1) push(list, s);

每次调用push时,您都会将s推入列表中。因此,每个条目都包含完全相同的指针值,这可能不是您想要的。
当您打印时,s包含“c”。每个节点都有一个指向s的指针,因此您会为每个节点打印三次“c”。

3
另外,scanf("%s", &s) 应该改为 scanf("%s", s),因为s是一个char数组。 - UnholySheep
2
同时,永远不要使用 scanf 进行任何操作。 - ceving
@ceving,您能否详细说明一下? - Tony
任何涉及到 scanf%s 的回答,必须 警告缺少宽度限制。否则就像使用 gets 一样糟糕。 - user694733

3

结构体中的data字段是一个指针,因此您需要将其视为指针。您没有正确使用它。尝试类似于以下内容:

node->data = strdup(newValue);

请查阅strdup的文档,了解它的工作原理。

此外,

if(list->firstNode != NULL){
    node->next = list->firstNode;
    list->firstNode = node;
}
else list->firstNode = node;

可以更改为

if(list->firstNode != NULL){
    node->next = list->firstNode;
}
list->firstNode = node;

甚至更好的是,只需…
node->next = list->firstNode;
list->firstNode = node;

另一个评论是,你应该选择在createNode内或外部设置node->nextNULL。不要两者都做。那只会使你的代码混乱。我会选择在createNode中这样做。我也会在同一个函数中将node->data设置为NULL。它是一个指针,应该被视为这样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接