Search code examples
cmalloctokenize

My program is not deallocating space correctly


I have this code here that is tokenizing a string. This line check if a string have quotes and returns the string without quotes. If I get something from the user, for example Hello "nice" day, it will return hello nice day:

char* str = take_quotes(line, stderr);

The function take_quotes is allocating space so I need to deallocate space in my tokenize function.

I thought that after I use the line I could free the space before returning my tokens, but when I do that I lose whatever is in my tokens. How should I free the space that was returned from take_quotes?

char** tokenize(char* line){
    if(line == NULL)
        return NULL;

    char* str = take_quotes(line, stderr); //free space

    char **tokens = malloc( sizeof( char * ) );
    *tokens = NULL;
    size_t n = 1;

    const char *delim = " \t \n";

    char *p = strtok( str, delim );

    int success = p != NULL;

    while ( success )
    {
        char **tmp = realloc( tokens, ( n + 1 ) * sizeof( char * ) );

        if ( tmp == NULL )
        {
            free( tokens );
            tokens = NULL;

            success = 0;
        }
        else
        {
            tokens = tmp;

            tokens[n - 1] = p;
            tokens[n] = NULL;
            ++n;

            p = strtok( NULL, delim );

            success = p != NULL;
        }
    }

    free(str);
    str = NULL;

    return tokens;
}

When I change my line

tokens[n - 1] = p;

to

tokens[n - 1] = strdup(p);

I get many errros in Valgrind

==8175== LEAK SUMMARY:
==8175==    definitely lost: 12 bytes in 3 blocks
==8175==    indirectly lost: 0 bytes in 0 blocks
==8175==      possibly lost: 6 bytes in 1 blocks
==8175==    still reachable: 0 bytes in 0 blocks
==8175==         suppressed: 0 bytes in 0 blocks
==8175== 
==8175== For lists of detected and suppressed errors, rerun with: -s
==8175== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Solution

  • strtok tokenizes the given string in-place, by inserting NUL terminating bytes into the string at the delimiters. No additional memory is allocated. When you free str, you are destroying the memory that contains these tokens.

    One approach is to make copies of your tokens, allocating memory for each along the way.

    strdup can be used for this purpose:

    tokens[n - 1] = strdup(p);
    

    If strdup is not available on your system, then you will have to rewrite its functionality (hint: strlen + malloc + strcpy, and don't forget additional space for the NUL terminating byte!).

    When it is time to free tokens, you will have to free each of the individual elements it contains first.


    A contrived example of freeing each element of a jagged array:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void free_tokens(char **tokens) {
        for (char **t = tokens; *t != NULL; t++)
            free(*t);
    
        free(tokens);
    }
    
    int main(void) {
        char **tokens = malloc(3 * sizeof *tokens);
        tokens[0] = strdup("hello");
        tokens[1] = strdup("world");
        tokens[2] = NULL;
    
        for (char **t = tokens; *t != NULL; t++)
            printf("token:[%s]\n", *t);
    
        free_tokens(tokens);
    }