LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-25-2024, 03:56 PM   #1
lxs602
Member
 
Registered: Oct 2018
Distribution: Ubuntu 24.04
Posts: 38

Rep: Reputation: 1
Freeing memory in recursive function: free(): double free detected in tcache2 (beginner question)


Hi

I have a section of code that should loop through a hash table and delete a linked list if present, for a cs50 problem set.

Code:
// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
} node;

// Number of buckets
const unsigned int N = 500041;

// Hash table
node *table[N];

// ... lines omitted

bool unload(void)
{
     // Uncomment for loop method
     // node *ptr, *ptr2;

    for (int a = 0; a < (N + 1); a++)
    {
       // Loop method 
       /*
        ptr = table[a];
        while(ptr != NULL)
        {
            ptr2 = ptr;
            ptr = ptr->next;
            free(ptr2);
        }
       */

        // Recursion method
        if (table[a] != NULL)
        {
            delete_node(table[a]);
        }
    }

    // used in separate functions earlier in code:
    free(counter);
    return (!fclose(dictfile));
}

void delete_node(node *ptr)
{
    if (ptr != NULL)
    {
        delete_node(ptr->next);
        free(ptr);
    }
    
    return;
}
I have a looped method (commented out) and a recursive method, but both give me the error in the subject line, which shows I am trying to free the same node twice.

I assume I have misunderstood something. What have I got wrong?

I have written what I think ought to be happening in pseudocode below:

- Recursive method: delete_node:
Quote:
1. If pointer is not null, then run delete_node again with the pointer to the next node.
2. This will run recursively, passing along the linked list, until null is reached. when delete_node will return.
3. The second-to-last call to delete_node will then run, and free the final node in the linked list, and return, allowing the third-to-last call to delete_node to run, and so on down the stack.
- Looped method:
Quote:
1. Until ptr reaches null, at the end of the linked list,
2. Point ptr2 to ptr (e.g. to memory address "0x100")
3. Point ptr to the next node in the linked list (e.g. at memory address "0x101"), while ptr2 remains pointing to where ptr was (at "0x100") (is this right?)
4. Now free memory at ptr2 (at "0x100"), then while loop runs again for ptr (at "0x101"), if it is not null.
Thank you

Last edited by lxs602; 04-25-2024 at 04:01 PM.
 
Old 04-25-2024, 07:27 PM   #2
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,269
Blog Entries: 24

Rep: Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196Reputation: 4196
This doesn't look right:

Code:
for (int a = 0; a < (N + 1); a++){...}
How big is that table... ?

But that is probably not the source of a double free().

As posted your recursion and loop methods should work, although I would NULL the free()'d hash table locations.

Are you confident of your insertion methods? Have you maybe entered the same hash in two locations or cross-linked two or more lists somehow?

Last edited by astrogeek; 04-25-2024 at 07:31 PM.
 
1 members found this post helpful.
Old 04-25-2024, 08:24 PM   #3
murugesandins
Member
 
Registered: Apr 2024
Location: Bangalore Karnataka India
Distribution: CYGWIN_NT
Posts: 61

Rep: Reputation: 0
My sample code using your code
Code:
#include <malloc.h>     // free
#include <stdio.h>
#define LENGTH 1023
char* counter = NULL;
FILE* dictfile = NULL;
// Represents a node in a hash table
typedef struct node
{
        char word[LENGTH + 1];
        struct node *next;
}node;
void delete_node(node *ptr);
// Number of buckets
const unsigned int N = 500041;
// Hash table
node* table[N];
// ... lines omitted
bool unload(void)
{
        #ifdef DEBUG
                printf( "Function: unload      START %s %02d\n", __FILE__, __LINE__);
        #endif
        // Uncomment for loop method
        // node *ptr, *ptr2;
        for( unsigned int a = 0; (N+1) > a; ++a)
        // for( int a = 0; (N+1) > a; ++a)
        {
                // Loop method
                /*
                ptr = table[a];
                while(ptr != NULL)
                {
                ptr2 = ptr;
                ptr = ptr->next;
                free(ptr2);
                }
                */

                // Recursion method
                // if( NULL != table[(unsigned int)a] )
                if( NULL != table[a] )
                {
                        delete_node(table[a]);
                }
        }
        // used in separate functions earlier in code:
        if( NULL != counter )
        {
                free(counter);
        }
        int dictFileRet = 0;
        if ( NULL != dictfile )
        {
                dictFileRet = fclose(dictfile);
                dictfile = NULL;
        }
        #ifdef DEBUG
                printf( "Function: unload      END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return dictFileRet;
}
void delete_node(node *ptr)
{
        #ifdef DEBUG
                printf( "Function: delete_node START %s %02d\n", __FILE__, __LINE__);
        #endif
        if (ptr != NULL)
        {
                delete_node(ptr->next);
                free(ptr);
        }
        #ifdef DEBUG
                printf( "Function: delete_node END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return;
}
int main()
{
        node* NullPtr = NULL;
        unload();
        delete_node(NullPtr);
        delete_node(NullPtr);
        delete_node(NullPtr);
        return 0;
}
/*
01)
always use:
        if ( 12 != variable )
instead of using
        if ( variable != 12 )
new joinee may miss ! operator
02)
        reset back to NULL after using free
03)
        when using malloc validate malloc did not fail
04)
        ++a better than a++ related to object oriented programming.
05)
        free only if not NULL
06)
        initialize the variable at the location of definition.
07)
        Compile using -g option to debug program/core dump files
08)
        $ #Following output was obtained using old source code.
        $ ./a.out
        Function: unload      START tcache2.C 21
        Function: unload      END   tcache2.C 56
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
        $ gcc.exe -g tcache2.C -o ./a.out -DDEBUG
*/

Last edited by murugesandins; 04-27-2024 at 08:53 AM. Reason: a.out output inside source file was obtained using old source code.
 
Old 04-25-2024, 09:11 PM   #4
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,784

Rep: Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083
Quote:
Originally Posted by murugesandins View Post
05)
free only if not NULL
This isn't needed, free() already checks for NULL pointer and does nothing in that case.
 
1 members found this post helpful.
Old 04-25-2024, 11:02 PM   #5
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,869
Blog Entries: 1

Rep: Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870
Code:
for (int a = 0; a < N; a++)
    {
        node *ptr, *ptr2 = table[a];
        table[a] = NULL;
        while((ptr = ptr2) != NULL)
        {
            ptr2 = ptr->next;
            memset(ptr, 0, sizeof *ptr):
            free(ptr);
        }
    }

Last edited by NevemTeve; 04-25-2024 at 11:04 PM.
 
Old 04-26-2024, 02:16 AM   #6
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,910

Rep: Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318
you can use valgrind to check the code, you can add more logging to see what's going on and also you can debug your code (and compile with -g). The main question is the value of a.
I don't know how do you create that table array, usually table[N+1] is invalid, as it was already mentioned.
 
Old 04-26-2024, 04:15 PM   #7
lxs602
Member
 
Registered: Oct 2018
Distribution: Ubuntu 24.04
Posts: 38

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by astrogeek View Post
This doesn't look right:

Code:
for (int a = 0; a < (N + 1); a++){...}
How big is that table... ?

But that is probably not the source of a double free().

As posted your recursion and loop methods should work, although I would NULL the free()'d hash table locations.
I forgot about indexing from zero, so that should be N, not (N + 1). N is table size which is 500,041.

Thanks for the input, I did not know if I had done it right.

Coming back to it today, I tried another approach in case I was messing something up with the second pointer, which ~worked~ *did not work:
Code:
        // Nested loop method
        
         while (table[a] != NULL)
         {
            ptr = table[a];
         
            while (ptr != NULL)
            {         
                ptr = ptr->next;
            }
          free(ptr);
          a++;
        }
When I went back to the to the other two methods, they are now working, and I don't know why. As far as I know, I made no changes except to add the new code above, and a couple of suggestions made here.

Earlier in the day yesterday, I found that the programme alternated between two different outputs when run in short succession (without no recompiling etc.). As a spell-check programme, it switched between finding 6, and then occasionally 4, misspelled words, on the same input and dictionary file.

Are the memory leaks affecting this? I am using the remote Visual Studio environment provided by cs50/github at https://cs50.dev .

For reference, the full code of dictionary.c is below. (Other files in the programme are provided - links below for reference)

Code:
// Implements a dictionary's functionality

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

#include "dictionary.h"

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
} node;

void delete_node(node *ptr);

char wordlc[LENGTH + 1];

// TODO: Choose number of buckets in hash table - lowest prime number > 500,000
const unsigned int N = 500041;

// Hash table
node *table[N];

// Dictionary word counter
unsigned int *dictword_counter;

// Loaded dictionary file
FILE *dictfile;

// Returns true if word is in dictionary, else false
bool check(const char *word)
{

    // Convert word to lowercase
    short counter = 0;

    while (*word != '\0')
    {
        wordlc[counter] = tolower(*word++);
        counter++;
    }
    wordlc[counter] = '\0';

    // Search linked list in the array at the hashed lowercase word
    node *temp_node = table[hash(wordlc)];

    while (temp_node != NULL)
    {
        if (!strcmp(wordlc, temp_node->word))
        {
            return true;
        }

        temp_node = temp_node->next;
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // Hash function inspired by djb2 (http://www.cse.yorku.ca/~oz/hash.html)
    // Cycle through *word characters until "\0" null character at end of word

    short counter = 0;

    while (*word != '\0')
    {
        wordlc[counter] = tolower(*word++);
        counter++;
    }
    wordlc[counter] = '\0';

    // magic number 52711 is prime
    unsigned int hash = 52711;
    int i = 1;

    for (short b = 1; i != 0; b++)
    {
        // hash * 127, but written bitwise for speed
        hash = ((hash << 7) - hash) + i;
        i = wordlc[b];
    }

    // Use remainder (modulo %) of division by number of buckets N, so output is less than N.
    hash = hash % N;
    // printf("return hash: %u\n", hash);
    return hash;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // Allocate memory for dict_word counter;
    dictword_counter = malloc(sizeof(unsigned int));
    if (dictword_counter == NULL)
    {
        printf("Could not allocate memory to load dictionary file.\n");
        return 0;
    }

    // Open dictionary
    dictfile = fopen(dictionary, "r");

    if (dictfile == NULL)
    {
        printf("Could not open dictionary file.\n");
        return 0;
    }

    // Read each word from dictionary file and load to hash table

    // Also count the number of dictionary words, for later
    *dictword_counter = 0;

    char word_buffer[LENGTH];
    while (fscanf(dictfile, "%s", word_buffer) != EOF)
    {
        // Allocate memory for new node and check success
        // printf("%s\n", word_buffer);
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
            printf("Failed to allocate memory to new node.\n");
            return 0;
        }

        // Define node word from the buffer
        strcpy(n->word, word_buffer);
        unsigned int i = hash(word_buffer);
        // printf("hash value (modulo N): %i\n", i);

        // Prepend new node to location of hash in the array:

        // 1. Point the node to the first node in the linked list
        n->next = table[i];

        // 2. Point the table array to the new node, which is now first in the linked list.
        table[i] = n;

        // Not " *dictword_counter++; ", as returns "expression result unused" error
        *dictword_counter = *dictword_counter + 1;
    }

    return 1;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // TODO
    return *dictword_counter;
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // Uncomment for loop methods
    // node *ptr, *ptr2;

    for (int a = 0; a < N; a++)
    {
        // Loop through table with two pointers (working)
        /*ptr = table[a];
        while (ptr != NULL)
        {
            ptr2 = ptr;
            ptr = ptr->next;
            free(ptr2);
        }*/

        // Double loop method
        /* printf("a: %i\n", a);
         while (table[a] != NULL)
         {

         ptr = table[a];
         printf("a: %i\n", a);
         while (ptr != NULL)
         {
             // ptr2 = ptr;
             ptr = ptr->next;
             printf("*ptr: %s\n", ptr->word);
             printf("*ptr: %p\n", &ptr);
         }
          free(ptr);
         a++;
        } */

        // Recursion method
        delete_node(table[a]);
    }

    free(dictword_counter);
    return (!fclose(dictfile));
}

void delete_node(node *ptr)
{
    if (ptr != NULL)
    {
        delete_node(ptr->next);
    }
    //strcpy(ptr->word, '\0');
    ptr->next = NULL;
    free(ptr);
    return;
}

Last edited by lxs602; 04-27-2024 at 07:05 AM.
 
Old 04-26-2024, 04:24 PM   #8
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,869
Blog Entries: 1

Rep: Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870
Try to find out what `delete_node(NULL)` does (in post #7)
 
Old 04-26-2024, 04:29 PM   #9
lxs602
Member
 
Registered: Oct 2018
Distribution: Ubuntu 24.04
Posts: 38

Original Poster
Rep: Reputation: 1
Source files not for editing by students:
from https://cdn.cs50.net/2023/fall/psets/5/speller.zip

dictionary.h
Code:
// Declares a dictionary's functionality

#ifndef DICTIONARY_H
#define DICTIONARY_H

#include <stdbool.h>

// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45

// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);

#endif // DICTIONARY_H
speller.c
Code:
// Implements a spell-checker

#include <ctype.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "dictionary.h"

// Undefine any definitions
#undef calculate
#undef getrusage

// Default dictionary
#define DICTIONARY "dictionaries/large"

// Prototype
double calculate(const struct rusage *b, const struct rusage *a);

int main(int argc, char *argv[])
{
    // Check for correct number of args
    if (argc != 2 && argc != 3)
    {
        printf("Usage: ./speller [DICTIONARY] text\n");
        return 1;
    }

    // Structures for timing data
    struct rusage before, after;

    // Benchmarks
    double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;

    // Determine dictionary to use
    char *dictionary = (argc == 3) ? argv[1] : DICTIONARY;

    // Load dictionary
    getrusage(RUSAGE_SELF, &before);
    bool loaded = load(dictionary);
    getrusage(RUSAGE_SELF, &after);

    // Exit if dictionary not loaded
    if (!loaded)
    {
        printf("Could not load %s.\n", dictionary);
        return 1;
    }

    // Calculate time to load dictionary
    time_load = calculate(&before, &after);

    // Try to open text
    char *text = (argc == 3) ? argv[2] : argv[1];
    FILE *file = fopen(text, "r");
    if (file == NULL)
    {
        printf("Could not open %s.\n", text);
        unload();
        return 1;
    }

    // Prepare to report misspellings
    printf("\nMISSPELLED WORDS\n\n");

    // Prepare to spell-check
    int index = 0, misspellings = 0, words = 0;
    char word[LENGTH + 1];

    // Spell-check each word in text
    char c;
    while (fread(&c, sizeof(char), 1, file))
    {
        // Allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // Append character to word
            word[index] = c;
            index++;

            // Ignore alphabetical strings too long to be words
            if (index > LENGTH)
            {
                // Consume remainder of alphabetical string
                while (fread(&c, sizeof(char), 1, file) && isalpha(c));

                // Prepare for new word
                index = 0;
            }
        }

        // Ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // Consume remainder of alphanumeric string
            while (fread(&c, sizeof(char), 1, file) && isalnum(c));

            // Prepare for new word
            index = 0;
        }

        // We must have found a whole word
        else if (index > 0)
        {
            // Terminate current word
            word[index] = '\0';

            // Update counter
            words++;

            // Check word's spelling
            getrusage(RUSAGE_SELF, &before);
            bool misspelled = !check(word);
            getrusage(RUSAGE_SELF, &after);

            // Update benchmark
            time_check += calculate(&before, &after);

            // Print word if misspelled
            if (misspelled)
            {
                printf("%s\n", word);
                misspellings++;
            }

            // Prepare for next word
            index = 0;
        }
    }

    // Check whether there was an error
    if (ferror(file))
    {
        fclose(file);
        printf("Error reading %s.\n", text);
        unload();
        return 1;
    }

    // Close text
    fclose(file);

    // Determine dictionary's size
    getrusage(RUSAGE_SELF, &before);
    unsigned int n = size();
    getrusage(RUSAGE_SELF, &after);

    // Calculate time to determine dictionary's size
    time_size = calculate(&before, &after);

    // Unload dictionary
    getrusage(RUSAGE_SELF, &before);
    bool unloaded = unload();
    getrusage(RUSAGE_SELF, &after);

    // Abort if dictionary not unloaded
    if (!unloaded)
    {
        printf("Could not unload %s.\n", dictionary);
        return 1;
    }

    // Calculate time to unload dictionary
    time_unload = calculate(&before, &after);

    // Report benchmarks
    printf("\nWORDS MISSPELLED:     %d\n", misspellings);
    printf("WORDS IN DICTIONARY:  %d\n", n);
    printf("WORDS IN TEXT:        %d\n", words);
    printf("TIME IN load:         %.2f\n", time_load);
    printf("TIME IN check:        %.2f\n", time_check);
    printf("TIME IN size:         %.2f\n", time_size);
    printf("TIME IN unload:       %.2f\n", time_unload);
    printf("TIME IN TOTAL:        %.2f\n\n",
           time_load + time_check + time_size + time_unload);

    // Success
    return 0;
}

// Returns number of seconds between b and a
double calculate(const struct rusage *b, const struct rusage *a)
{
    if (b == NULL || a == NULL)
    {
        return 0.0;
    }
    else
    {
        return ((((a->ru_utime.tv_sec * 1000000 + a->ru_utime.tv_usec) -
                  (b->ru_utime.tv_sec * 1000000 + b->ru_utime.tv_usec)) +
                 ((a->ru_stime.tv_sec * 1000000 + a->ru_stime.tv_usec) -
                  (b->ru_stime.tv_sec * 1000000 + b->ru_stime.tv_usec)))
                / 1000000.0);
    }
}
makefile
Code:
speller:
	clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c -o speller.o speller.c
	clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c -o dictionary.o dictionary.c
	clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -o speller speller.o dictionary.o -lm
 
Old 04-27-2024, 02:14 AM   #10
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,910

Rep: Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318Reputation: 7318
Code:
         while (table[a] != NULL)
         {
            ptr = table[a];
         
            while (ptr != NULL)
            {         
                ptr = ptr->next;
            }
          free(ptr);     // ptr is always NULL here
          a++;
        }
This is wrong. It won't free anything.
 
2 members found this post helpful.
Old 04-27-2024, 08:22 AM   #11
lxs602
Member
 
Registered: Oct 2018
Distribution: Ubuntu 24.04
Posts: 38

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by murugesandins View Post
My sample code using your code
Code:
#include <malloc.h>     // free
#include <stdio.h>
#define LENGTH 1023
char* counter = NULL;
FILE* dictfile = NULL;
// Represents a node in a hash table
typedef struct node
{
        char word[LENGTH + 1];
        struct node *next;
}node;
void delete_node(node *ptr);
// Number of buckets
const unsigned int N = 500041;
// Hash table
node* table[N];
// ... lines omitted
bool unload(void)
{
        #ifdef DEBUG
                printf( "Function: unload      START %s %02d\n", __FILE__, __LINE__);
        #endif
        // Uncomment for loop method
        // node *ptr, *ptr2;
        for( unsigned int a = 0; (N+1) > a; ++a)
        // for( int a = 0; (N+1) > a; ++a)
        {
                // Loop method
                /*
                ptr = table[a];
                while(ptr != NULL)
                {
                ptr2 = ptr;
                ptr = ptr->next;
                free(ptr2);
                }
                */

                // Recursion method
                // if( NULL != table[(unsigned int)a] )
                if( NULL != table[a] )
                {
                        delete_node(table[a]);
                }
        }
        // used in separate functions earlier in code:
        if( NULL != counter )
        {
                free(counter);
        }
        int dictFileRet = 0;
        if ( NULL != dictfile )
        {
                dictFileRet = fclose(dictfile);
                dictfile = NULL;
        }
        #ifdef DEBUG
                printf( "Function: unload      END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return dictFileRet;
}
void delete_node(node *ptr)
{
        #ifdef DEBUG
                printf( "Function: delete_node START %s %02d\n", __FILE__, __LINE__);
        #endif
        if (ptr != NULL)
        {
                delete_node(ptr->next);
                free(ptr);
        }
        #ifdef DEBUG
                printf( "Function: delete_node END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return;
}
int main()
{
        node* NullPtr = NULL;
        unload();
        delete_node(NullPtr);
        delete_node(NullPtr);
        delete_node(NullPtr);
        return 0;
}
/*
01)
always use:
        if ( 12 != variable )
instead of using
        if ( variable != 12 )
new joinee may miss ! operator
02)
        reset back to NULL after using free
03)
        when using malloc validate malloc did not fail
04)
        ++a better than a++ related to object oriented programming.
05)
        free only if not NULL
06)
        initialize the variable at the location of definition.
07)
        Compile using -g option to debug program/core dump files
08)
        $ gcc.exe -g tcache2.C -o ./a.out -DDEBUG
        $ ./a.out
        Function: unload      START tcache2.C 21
        Function: unload      END   tcache2.C 56
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
        Function: delete_node START tcache2.C 63
        Function: delete_node END   tcache2.C 71
*/

What do 63 and 71 mean at "Function: delete_node START tcache2.C 63"?

How do I fix below?

Code:
/speller$ gcc -g tcache2.c -o ./a.out -DDEBUG dictionary2.c 
cc1: fatal error: tcache2.c: No such file or directory
compilation terminated.

Last edited by lxs602; 04-27-2024 at 08:28 AM.
 
Old 04-27-2024, 08:51 AM   #12
murugesandins
Member
 
Registered: Apr 2024
Location: Bangalore Karnataka India
Distribution: CYGWIN_NT
Posts: 61

Rep: Reputation: 0
Earlier, I have written old output inside that source code.
Pasting updated code using updated output:
Code:
printf( "Function: delete_node END   %s %02d\n", __FILE__, __LINE__);
Hence this was displaying:
Function: delete_node END tcache2.C Old line number
Updated code
Code:
$ /cygdrive/c/Users/murugesandins/cygwin/bin/cat.exe tcache2.C
#include <malloc.h>     // free
#include <stdio.h>
#define LENGTH 1023
char* counter = NULL;
FILE* dictfile = NULL;
// Represents a node in a hash table
typedef struct node
{
        char word[LENGTH + 1];
        struct node *next;
}node;
void delete_node(node *ptr);
// Number of buckets
const unsigned int N = 500041;
// Hash table
node* table[N];
// ... lines omitted
bool unload(void)
{
        #ifdef DEBUG
                printf( "Function: unload      START %s %02d\n", __FILE__, __LINE__);
        #endif
        // Uncomment for loop method
        // node *ptr, *ptr2;
        for( unsigned int a = 0; (N+1) > a; ++a)
        // for( int a = 0; (N+1) > a; ++a)
        {
                // Loop method
                /*
                ptr = table[a];
                while(ptr != NULL)
                {
                ptr2 = ptr;
                ptr = ptr->next;
                free(ptr2);
                }
                */

                // Recursion method
                // if( NULL != table[(unsigned int)a] )
                if( NULL != table[a] )
                {
                        delete_node(table[a]);
                }
        }
        // used in separate functions earlier in code:
        if( NULL != counter )
        {
                free(counter);
        }
        int dictFileRet = 0;
        if ( NULL != dictfile )
        {
                dictFileRet = fclose(dictfile);
                dictfile = NULL;
        }
        #ifdef DEBUG
                printf( "Function: unload      END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return dictFileRet;
}
void delete_node(node *ptr)
{
        #ifdef DEBUG
                printf( "Function: delete_node START %s %02d\n", __FILE__, __LINE__);
        #endif
        if (ptr != NULL)
        {
                delete_node(ptr->next);
                free(ptr);
        }
        #ifdef DEBUG
                printf( "Function: delete_node END   %s %02d\n", __FILE__, __LINE__);
        #endif
        return;
}
int main()
{
        node* NullPtr = NULL;
        unload();
        delete_node(NullPtr);
        delete_node(NullPtr);
        delete_node(NullPtr);
        return 0;
}
/*
01)
always use:
        if ( 12 != variable )
instead of using
        if ( variable != 12 )
new joinee may miss ! operator
02)
        reset back to NULL after using free
03)
        when using malloc validate malloc did not fail
04)
        ++a better than a++ related to object oriented programming.
05)
        free only if not NULL
06)
        initialize the variable at the location of definition.
07)
        Compile using -g option to debug program/core dump files
08)
        $ gcc.exe -g tcache2.C -o ./a.out -DDEBUG
        $ ./a.out
Function: unload      START tcache2.C 21
Function: unload      END   tcache2.C 58
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
*/

Code:
$ /cygdrive/c/Users/murugesandins/cygwin/bin/gcc.exe tcache2.C -DDEBUG dictionary2.cc -o ./a.out
$ /usr/bin/file.exe tcache2.C dictionary2.cc
tcache2.cc:     C source, ASCII text
dictionary2.cc: empty
$# I tried sample program hence I am using dictionary2.cc empty file.
$  ./a.out
Function: unload      START tcache2.C 21
Function: unload      END   tcache2.C 58
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
Function: delete_node START tcache2.C 65
Function: delete_node END   tcache2.C 73
Quote:
free only if not NULL
I have seen these kind of exception when I have used nwrfcsdk during 2012 SAP library.
Hence this is also required when parsing IDOC files and using external libraries.
 
Old 04-27-2024, 03:59 PM   #13
lxs602
Member
 
Registered: Oct 2018
Distribution: Ubuntu 24.04
Posts: 38

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by pan64 View Post
Code:
         while (table[a] != NULL)
         {
            ptr = table[a];
         
            while (ptr != NULL)
            {         
                printf("*ptr: %s\n", ptr->word);           // lines in bold added
                printf("ptr: %p\n", ptr);
 
                if (ptr->next == NULL)    
                   { 
                       printf("marker\n");
                       memset(ptr, 0, sizeof *ptr)
                    // strcpy(ptr, '\0')                      // this line doesn't work
                    // strcpy(ptr.word, '\0')                 // this line also doesn't work
                       free(ptr)
                   }
                 else
                 { 
                 ptr = ptr->next;
                 }
            }
          // free(ptr);     // this line deleted
          a++;
        }
This is wrong. It won't free anything.
I know this method is not the best way, but I tried to correct just as an exercise. How can I make ptr NULL to escape the while loop above?

strcpy(ptr, '\0') above fails with "null passed to a callee that requires a non-null argument".

I hope this isn't a stupid question.

printf() output:
Quote:
*ptr: caterpillar
ptr: 0x650c7ee5a4f0
marker
*ptr: Z��P
ptr: 0x650c7ee5a4f0
marker
*ptr: �J".
ptr: 0x650c7ee5a4f0
marker

Last edited by lxs602; 04-27-2024 at 04:03 PM.
 
Old 04-27-2024, 07:50 PM   #14
sundialsvcs
LQ Guru
 
Registered: Feb 2004
Location: SE Tennessee, USA
Distribution: Gentoo, LFS
Posts: 10,668
Blog Entries: 4

Rep: Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945
One piece of "vital(!) battle wisdom" that I long-ago learned was: "as soon as you 'free' something that was referred-to by a pointer, immediately set that pointer (and all copies of it ...) to NULL/nil."

"Now, every reference to the storage is gone: you just made sure of that."

The "nanoseconds" required to perform this "extra" step literally do not matter, but your code will "in a stroke" become much more reliable. Because: "one piece of code" no longer has to worry about what "some other piece of code" might later be doing.

(Every "free()" routine knows to ignore a NULL/nil pointer ...)

Last edited by sundialsvcs; 04-27-2024 at 07:54 PM.
 
Old 04-28-2024, 06:02 AM   #15
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,869
Blog Entries: 1

Rep: Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870Reputation: 1870
@OP After every modification, run your program with valgrind.
 
  


Reply

Tags
beginner, clanguage, pointers



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Why a regular grammar cannot be both left-recursive, and right-recursive? ajiten Programming 17 08-15-2023 01:25 PM
[SOLVED] Freeing allocated memory in a recursive data structure Gullible Jones Programming 1 08-29-2013 03:51 AM
[SOLVED] Freeing dynamically allocated memory in a C recursive function marian57 Programming 7 09-11-2011 06:58 PM
non Recursive query and Recursive query prashsharma Linux - Server 1 06-27-2007 09:33 AM
Freeing memory allocated in a function con Programming 4 01-02-2006 03:25 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:11 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration