【问题标题】:Linked list of strings has the same strings for each node字符串链表对于每个节点都有相同的字符串
【发布时间】:2017-12-18 07:08:57
【问题描述】:

我正在尝试创建一个链表,其中每个节点都存储一个字符串,但我遇到了一个问题,即每个节点最终都在每个节点中存储了相同的确切字符串。在 main() 的末尾,我打印出存储在每个节点中的单词,它总是重复整个列表中输入的最后一个字符串。

我不知道发生了什么,因为如果我把它变成一个字符串,它工作得非常好,每个字符都存储在正确的节点中。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct wordnode {
    char *word;
    struct wordnode *next;
};

struct wordnode *link = NULL;

void addword(char *aword);

int main(void) {


    char *aword;
    int i;

    for(i = 0; i < 10; i++) {
        scanf(" %s", aword);
        addword(aword);
    }
    printf("\n");
    for(; link != NULL; link = link->next) {
        printf("|%s ", link->word);
    }

    printf("|\n");
    return 0;
}

void addword(char *aword) {
    struct wordnode *cur, *prev, *new_node;

    new_node = malloc(sizeof(struct wordnode));

    new_node->word = aword;

    for(cur = link, prev = NULL; cur != NULL; prev = cur, cur = cur->next) {
        ;
    }

    new_node->next = cur;

    if(prev == NULL) {
        link = new_node;
    } else {
        prev->next = new_node;
    }
}

【问题讨论】:

  • 您有多个问题。它从你如何使用scanf 传递一个未初始化的指针开始。 scanf 函数需要一些足够大的分配内存来写入输入。它本身不分配内存。
  • 你应该复制输入的字符串。
  • 指针不会神奇地创建内存。 char *aword 未初始化并指向未定义的位置。像这样使用它会调用未定义的行为。它应该是char aword[SIZE];,以便分配一个字符数组。而且您不应该简单地复制指针 (new_node-&gt;word = aword;),而是复制指向的位置(参见 strdup)。最后,在 C 中始终释放所有分配的内存是一种很好的做法。祝你练习 C 好运 ;-) ...
  • 您认为自己拥有的任何“有效”案例都是错觉。您正在破坏内存,以不可预知的方式破坏您的运行时环境。此代码不应按原样运行。
  • 一个问题是您没有复制每个字符串,因此您保存了一个指向您将用下一个输入覆盖的数组的指针,这没有帮助。确保复制字符串 - 小心。如果可用,请使用strdup()(如果不可用,编写自己的版本并不难——char *strdup(const char *str) { size_t len = strlen(str) + 1; char *copy = malloc(len); if (copy != 0) memmove(copy, str, len); return copy; }——如果你明白我的意思。)

标签: c string linked-list


【解决方案1】:

代码中有很多问题。其中一些已经被提及。代码将是这样的。代码末尾有说明。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define STR2(x) #x
#define STR(X) STR2(X)
#define MAXWORD 10
#define MAXWORDLEN 20

struct wordnode {
    char *word;
    struct wordnode *next;
};


struct wordnode* addword(char *aword, struct wordnode *link);
void printList(struct wordnode*link);
void freeList(struct wordnode *link);

int main(void) {
    char aword[MAXWORDLEN+1];

    struct wordnode *link = NULL;
    for(size_t i = 0; i < MAXWORD; i++) {
        if( scanf("%" STR(MAXWORDLEN) "s", aword[i]) == 1 ){
           link = addword(aword, link);
        }
        else{
            fprintf(stderr, "%s\n","Error in input" );
            exit(1);
        }
    }

    printList(link);  

    freeList(link);
    return 0;
}
void printList(struct wordnode*link){
    while(link){
        printf("%s \n", link->word);
        link  = link->next;
    }
}
void freeList(struct wordnode *link){
    struct wordnode *temp;
    while(link){
        temp = link;
        link = link->next;
        free(temp);
    }
}

struct wordnode* addword(char *aword, struct wordnode *link) {

    struct wordnode *new_node = malloc(sizeof(struct wordnode));

    if( new_node == NULL){
        fprintf(stderr, "%s\n", "Error in malloc");
        exit(1);
    }
    new_node->word = strdup( aword );
    if( new_node->word == NULL){
        fprintf(stderr, "%s\n", "Error in strdup" );
        exit(1);
    }
    new_node->next = NULL;

    if( link == NULL){
        return new_node;
    }
    struct wordnode *cur = link;
    while( cur->next != NULL ){
        cur = cur -> next;
    }
    cur->next = new_node;
    return link;
}

您想存储一些字符串(以 nul 结尾的字符数组),然后您想将它们添加到列表中。同样从您的示例实现中,您试图将其添加到尾部的列表中。

总结一下-

  • scanf 需要一个指向可以存储输入数据的内存的指针。但是你的没有初始化。

  • 其次,你复制字符串的方式,它只是一个浅拷贝(你让它指向一些已经存在的内存)。您需要使用strdupmalloc - memcpymalloc-strcpy 复制它。

  • 如果 POSIX strdup() 不可用,您可以使用 Jonathan Leffler 提到的内容。

  • 在这里您可以看到我们已经使用freeList() 函数释放了分配的内存。 当您使用完分配的内存时 - 释放内存。

  • 不要转换malloc的返回值。

  • 还要检查malloc是否成功检查它的返回值。

  • 您已将列表头用作全局变量。这里不需要。

【讨论】:

  • 谢谢,但我不能使用 strdup,我正在使用 ISO C。但是我使结构具有一定大小的数组的字符,因此它被初始化,而不是指针,然后调用scanf 在调用 malloc 后将字符串直接放入每个节点的数组中,这似乎工作得很好,这完全消除了 aword 变量。
【解决方案2】:

char * aword 未初始化和定位。应该是:

char aword[100];

(100只是一个数字,数组的大小。你可以用任何你想要的数字替换它)

【讨论】:

  • 如 cmets 中所述,这不是唯一的问题,请改进您的答案。
  • 这个答案对我非常有用,因为它简短而简单。它引导我找出其他问题。我以为 scanf 会自动将内存分配到一个字符串中,所以这就是我没有初始化 aword 的原因。
【解决方案3】:

您尚未为字符串分配内存。结果aword 将包含垃圾值,并将其传递给scanf 是未定义的行为。假设aword 具有0x7fffe4e0cdf0 并且您的scanf 将字符串存储在地址0x7fffe4e0cdf0 并将此地址传递给addword 函数并且您的结构成员word 也更新为相同的值。下一个 scanf 还将新值存储在 aword 指向的同一内存中并传递给函数。结果,所有链表中的word 都指向同一个内存位置。理想的解决方案是为每个被扫描的字符串分配内存并将其传递给 '`addword' 函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    相关资源
    最近更新 更多