Search code examples
cfunctionpointersrecursionglobal-variables

Global pointer to a structure doesn't updated in a function call when trying to access next node. Global pointer returns nil


Why in the first code any->next and root->next at the free_hashtable returns nil? If I print in the generate function, it does returns an address... So, as the *root is global, why I can't access it?

Also why in the second code when I recursive call a function that have a paramenter pointing to a global structure pointer (so I can navigate throught it), it just gives me the main address, if I try like function(any->next), the returned value is just the same as any not any->next.

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

const int nodes = 27; // 26 letters + '.
int height = 3;

typedef struct hash_table
{
    char word[46]; // 45 letters + NULL.
    struct hash_table *next;
}
hash_table;

typedef struct trie
{
    bool is_word;
    struct trie *character[nodes];
    hash_table *next;
}
trie;

trie *generate(int times);
void free_hashtable(trie *any, int times);
void free_hashtable2(hash_table *any);
void free_trie(trie *any);

trie *root;

int main(void)
{
    // Generate a trie with a height specified in a variable
    // and also allocate memory to the entire structure.
    root = generate(height);

    free_hashtable(root, height);
    free_trie(root);
    free(root);

    return 0;
}

trie *generate(int times)
{
    // Allocate memory to root, to character[i]
    // and also change the access of character[i] to root.
    // So in the second run, root would be equivalent to
    // root->character[i].
    root = malloc(sizeof(trie));

    //printf("%p\n", root); HERE it gives me an address.
    root->next = NULL;


    if (times > 0)
    {
        for (int i = 0; i < nodes; i++)
        {
            // For every single pointer generates more nodes pointers.
            root->character[i] = generate(times - 1);
        }
    }

    else
    {
        for (int i = 0; i < nodes; i++)
        {
            // Set all remaining pointers to null
            // as there aren't more arrays.
            root->character[i] = NULL;
        }
        // Malloc for new structure.
        root->next = malloc(sizeof(hash_table));
        root->next->next = NULL;
    }

    // Return the address of root,
    // so the root in the main function can point
    // to the trie created.
    return root;
}

void free_hashtable(trie *any, int times)
{
    printf("%p\n", root->character[0]); // print nil, yet, if I print in the generate function,
    // it does give me an address.

    printf("%p\n", any->character[0]); // returns nil, but why?
    // Shouldn't any = root? and then any->next = root->next?

    for (int i = 0; i < nodes; i++)
    {
        if (times > 1)
        {
            free_hashtable(any->character[i], times - 1);
        }
    }

    if (times == 1)
    {
        for (int i = 0; i < nodes; i++)
        {
            //Where root is root->character[i]->character[i]
            if (any->character[i]->next->next != NULL)
            {
                free_hashtable2(any->character[i]->next);
            }
            free(any->character[i]->next);
        }
    }

    return;
}

void free_hashtable2(hash_table *any)
{
    if (any->next != NULL)
    {
        free_hashtable2(any->next);
        free(any->next);
    }

    return;
}

void free_trie(trie *any)
{
    for (int i = 0; i < nodes; i++)
    {
        if (any->character[i] != NULL)
        {
            // Calls function first so it can works backwards.
            free_trie(any->character[i]);
            free(any->character[i]);
        }
    }

    return;
}
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef struct test
{
    char word[15];
    struct test *next;
}
test;

test *root;
test *generate(int times);
void free_dummy(test *any);

int main(void)
{
    root = generate(3);
    free_dummy(root);
    free(root);
}

test *generate(int times)
{
    root = malloc(sizeof(test));
    strcpy(root->word, "dummy");
    if (root == NULL)
    {
        return NULL;
    }
    if (times != 0)
    {
        root->next = generate(times - 1);
    }
    if (times == 0)
    {
        root->next = NULL;
    }
    return root;
}

void free_dummy(test *any) // Comes as root.
{
    printf("%p\n", any); // any never updates to root->next, but any is pointing to root. Shouldn't any->next = root->next?
    if (any->next != NULL)
    {
        free_dummy(any->next);
        free(any->next);
    }
    return;
}

Thanks to everyone! All help is welcome!


Solution

  • You call the function generate() recursively and two things happens you overwrite the global variable root = malloc(...) in each call (leaking memory) and in the last call when when times == 0 you reset root->characters[i] = NULL.

    I had to change nodes to a symbolic constant to get your code to compile, and changed height for consistency.

    In generate() use a local variable root instead. Prefer passing a variable opposed to type to sizeof. I didn't fix it but you should check that malloc() was successful.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    #define nodes 27 // 26 letters + '.
    #define height 3
    
    typedef struct hash_table {
        char word[46]; // 45 letters + NULL.
        struct hash_table *next;
    } hash_table;
    
    typedef struct trie {
        bool is_word;
        struct trie *character[nodes];
        hash_table *next;
    } trie;
    
    trie *generate(int times) {
        trie *root = malloc(sizeof *root);
        root->next = NULL;
        if(!times) {
            for (int i = 0; i < nodes; i++)
                root->character[i] = NULL;
            root->next = malloc(sizeof *root->next);
            root->next->next = NULL;
            return root;
        }
        for (int i = 0; i < nodes; i++)
            root->character[i] = generate(times - 1);
        return root;
    }
    
    void free_hashtable2(hash_table *any) {
        if (!any->next) return;
        free_hashtable2(any->next);
        free(any->next);
    }
    
    void free_trie(trie *any) {
        for (int i = 0; i < nodes; i++) {
            if (!any->character[i]) continue;
            free_trie(any->character[i]);
            free(any->character[i]);
        }
    }
    
    void free_hashtable(trie *any, int times) {
        printf("%p\n", (void *) any->character[0]);
        for (int i = 0; i < nodes; i++)
            if (times > 1)
                free_hashtable(any->character[i], times - 1);
        if (times == 1)
            for (int i = 0; i < nodes; i++) {
                if (any->character[i]->next->next)
                    free_hashtable2(any->character[i]->next);
                free(any->character[i]->next);
            }
    }
    
    int main(void) {
        trie *root = generate(height);
        free_hashtable(root, height);
        free_trie(root);
        free(root);
    }
    

    To me, the 3 different free functions is a code smell. Instead write one free function that does the "opposite" of your builder function generate().