简体   繁体   中英

Freeing a pointer (to a void*) inside of a struct

C newbie here, and I can't seem to figure this one out. So I'm starting to implement a linked-list (just something basic so I can wrap my head around it) and I've hit a snag. The program runs fine, but I can't free() the data stored in my struct.

Here's the source:

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

struct node {
    struct node* next;
    void* data;
    size_t data_size;
};
typedef struct node node;

node* create_node(void* data, size_t size)
{
    node* new_node = (node*)malloc(sizeof(node));
    new_node->data = (void*)malloc(size);
    new_node->data = data;
    new_node->next = NULL;
    return new_node;
}
void destroy_node(node** node)
{
    if(node != NULL)
    {
        free((*node)->next);
        //this line here causes the error
        free((*node)->data);
        free(*node);
        *node = NULL;
        printf("%s\n", "Node destroyed!");
    }
}

int main(int argc, char const *argv[])
{
    float f = 4.325;
    node *n;

    n = create_node(&f, sizeof(f));
    printf("%f\n", *((float*)n->data));

    if (n->next == NULL)
        printf("%s\n", "No next!");

    destroy_node(&n);
    return 0;
}

I get this message in the program output:

malloc: *** error for object 0x7fff5b4b1cac: pointer being freed was not allocated

I'm not entirely keen on how this can be dealt with.

This is because when you do:

new_node->data = data;

you replaces the value put by malloc just the line before. What you need is to copy the data, see the function memcpy

 node* create_node(void* data, size_t size) ... new_node->data = (void*)malloc(size); new_node->data = data; 

Here, (1) you are losing memory given by malloc because the second assignment replaces the address (2) storing a pointer of unknown origin.

Number two is important because you can't guarantee that the memory pointed to by data was actually malloced. This causes problems when freeing the data member in destroy_node . (In the given example, an address from the stack is being freed)

To fix it replace the second assignment with

memcpy (new_node->data, data, size);

You also have a potential double free in the destroy_node function because the next member is also being freed.

In a linked list, usually a node is freed after being unlinked from the list, thus the next node shouldn't be freed because it's still reachable from the predecessor of the node being unlinked.

While you got an answer for the immediate problem, there are numerous other issues with the code.

struct node {
    struct node* next;
    void* data;

What's up with putting * next to type name? You are using it inconsistently anyway as in main you got node *n.

    size_t data_size;
};
typedef struct node node;

node* create_node(void* data, size_t size)
{
    node* new_node = (node*)malloc(sizeof(node));

What are you casting malloc for? It is actively harmful. You should have used sizeof(*new_node). How about checking for NULL?

    new_node->data = (void*)malloc(size);

This is even more unnecessary since malloc returns void * so no casts are necessary.

    new_node->data = data;

The bug already mentioned.

    new_node->next = NULL;
    return new_node;
}
void destroy_node(node** node)
{
    if(node != NULL)
    {

How about:

if (node == NULL)
    return;

And suddenly you get rid of indenation for the entire function.

    free((*node)->next);
    //this line here causes the error
    free((*node)->data);
    free(*node);
    *node = NULL;
    printf("%s\n", "Node destroyed!");

What's up with %s instead of mere printf("Node destroyed!\\n")? This message is bad anyway since it does not even print an address of aforementioned node.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM