C语言中的链表

3

我在运行这个包含单词数据的链表实现时遇到了一些问题。问题是,当我尝试打印我插入的单词时,我什么也没有得到。我为此苦恼不已,不知道哪里出了错。我希望这不是什么愚蠢的错误。无论如何,以下是代码 -

typedef struct node
{
    void *data;
    struct node *next;
} NODE;

NODE *new_node(void *data)
{
    NODE *new = malloc(sizeof(NODE));
    if(new)
    {
        new->data = data;
        new->next = NULL;
        return new;
    }
    else
    {
        return NULL;
    }
}

void print_list(NODE *head, void (print_fn) (void*))
{
    if(head && head->next)
    {
        while(head->next)
        {
            if(print_fn)
                print_fn(head->data);
            else
                printf("Word: %s\n", (char *)head->data);
            head = head->next;
        }
    }
    return;
}

void append(NODE **head, NODE *node)                                                                                  
{
    NODE *tmp = *head;
    if(tmp && node)
    {
        while(tmp->next)
            tmp = tmp->next; 
        (*head)->next = node; /*add as last node*/
    }
    return;
}


NODE *create_list()
{
    FILE *dict_file = fopen("trial.txt", "r");

    if(dict_file)
    {
        NODE *head = new_node(NULL);
        if(!head) return NULL;

        char word[20];
        int first  = TRUE;
        memset(word, '\0', 20);

        while(fgets(word, sizeof(word), dict_file) != NULL )
        {
            if(first)
            {
                head->data = (void*)word;
                first = FALSE;
            }
            else
            {
                append(&head, new_node((void*)word));
            }
        }
        fclose(dict_file);
        return head;
    }
    else
    {
        printf("ERROR: File not found");
        return NULL;
    }
}

int main(int argc, char *argv[])
{
    NODE *head = create_list();

    if(!head)
    {
        printf("ERROR: Either malloc() failed or data not found\n");
        return FALSE;
    }
    else
    {
        print_list(head, NULL);
        return TRUE;
    }
}

5
一般而言,你应该避免将“new”作为变量名。我建议你可以选择其他的名称来代替。 - NG.
你忘了提供 "print_list" 函数的代码。 - user405725
2个回答

15

这个回答变得相当冗长。不要认为我是对你个人的攻击,但是你犯了很多初级错误。我在大学里遇到过很多需要帮助学习C语言和编程的人,所以我已经习惯于注意这些问题。

我发现的重要问题

  • 你给words赋了一个指向栈变量的指针
    这是非常错误的,因为一旦执行离开创建它的函数,该值将被覆盖。 解决方案:将该变量的内容复制到一个堆变量中。

  • 你的append函数有缺陷
    它将追加的元素添加到第二个位置而不是最后一个位置。请注意,您也不需要在末尾返回。在append方法中要求双指针输入也没有意义。在将head分配给tmp之后,检查tmp是否与NULL无济于事,因为如果head不是NULL,则它不会是NULL。我还建议检查新节点是否是NULL。如果是,则可以节省迭代整个集合的时间。

  • create_list函数不够优化
    首先,第一个和第二个情况之间的区别是没有意义的。引入另一个指针(在我的代码中称为current)将消除检查它是否是第一个或不是第一个的需要。接下来,您总是在head上调用append函数,因此您总是需要迭代整个集合。这也可以通过引入current变量来优化。 (开始时,应将其赋值为head的值。)

  • print_list函数有错误
    如果只有一个节点,则不会打印任何内容。它还多余地检查了指针是否为空。(循环的开始也进行了检查。)此void函数末尾的返回语句也是不必要的。

  • 当您不使用内存时,应该释放它
    @Baltasarq在他的答案中编写了一个不错的clear函数,您应该使用它:)

并不严重的错误,但你应该知道它们

  • void*不应该用于代替char* 如果您知道NODE结构的data成员将存储字符,为什么要使用void*?这是一个不好的实践! (当然,除非你有充分的理由。)

  • new用作变量名会使代码不符合C ++。因此,我建议反对。

  • 采用更好的编码风格,它将使您的代码易于阅读。

  • 不一致:如果在print_list中您没有分配新变量以遍历集合(如在append中所做的那样),则将参数命名为head是错误的。

    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    typedef struct node
    {
        void *data;
        struct node *next;
    } NODE;
    
    NODE *new_node(void *data)
    {
        NODE *newNode = (NODE*)malloc(sizeof(NODE));
        if (newNode)
        {
            newNode->data = data;
            newNode->next = NULL;
            return newNode;
        }
        return NULL;
    }
    
    void append(NODE *head, NODE *node)
    {
        if (head && node)
        {
            NODE *tmp = head;
            while (tmp->next)
                tmp = tmp->next; 
            tmp->next = node; /* add as last node */
        }
    }
    
    void print_list(NODE *node, void (print_fn) (void*))
    {
        while (node)
        {
            if (print_fn)
                print_fn(node->data);
            else
                printf("Word: %s\n", (char *)node->data);
    
            node = node->next;
        }
    }
    
    NODE *create_list()
    {
        FILE *dict_file = fopen("trial.txt", "r");
    
        if (dict_file)
        {
            NODE *head = NULL;
            NODE *current = head;
    
            char word[20];
            memset(word, '\0', 20);
    
            while (fgets(word, sizeof(word), dict_file))
            {
                // Creating a variable on the heap
                char *data = calloc(sizeof(word) + 1, sizeof(char));
                // Copying the contents of words to it
                strcpy(data, word);
    
                append(current, new_node((void*)data));
                if (current->next)
                    current = current->next
            }
            fclose(dict_file);
            return head;
        }
        else
        {
            printf("ERROR: File not found");
        }
        return NULL;
    }
    
    int main(int argc, char *argv[])
    {
        NODE *head = create_list();
    
        if (!head)
        {
            printf("ERROR: Either malloc() failed or data not found\n");
        }
        else
        {
            print_list(head, NULL);
        }
        return 0;
    }
    

@movieyoda - 有道理。你的应用程序能够与我的代码配合使用吗? - Venemo
你还应该考虑清除列表,释放所有节点和数据指针。 - Baltasarq
@Baltasarq - 应用程序分配的内存在应用程序退出时会自动释放。 - Venemo
@Venemo:那不是真的。有很多情况下操作系统无法释放分配给进程的内存。据我所知,这并不包括 malloc,但确实包括像全局 COM 堆之类的东西。假设操作系统会在你之后清理垃圾是一个养成不良习惯。 - Puppy
@Venemo:太好了。所以你想要一个泄漏内存的例子?那太完美了。 - Puppy
显示剩余6条评论

1

要小心,因为当内存不足时,malloc()和其衍生函数可能会返回一个NULL指针。实际上,还需要一个clear()方法来释放所有数据以及节点本身。

void clear(NODE *node)
{
    NODE * temp = NULL;

    while( node != NULL ) {
       temp = node->next;
       free( node->data );
       free( node );

       node = temp;
    }
}

你的main()函数应该返回一个退出码给操作系统,可以是EXIT_SUCCESS或者EXIT_FAILURE,而不是TRUE或FALSE。
int main(int argc, char *argv[])
{
    NODE *head = create_list();
    int toret = EXIT_SUCCESS;

    if(!head)
    {
        printf("ERROR: Either malloc() failed or data not found\n");
        toret = EXIT_FAILURE;
        clear( head );
    }
    else
    {
        print_list(head, NULL);
        clear( head );
    }

    return toret;
}

+1 感谢您编写那个 clear 函数!我在我的答案中添加了他应该检查您的函数。 :) - Venemo

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