Search code examples
cmemory-leakstokenize

Memory leak from malloc through tokenise function in C


I'm encountering a memory leak when I exit my program (found using valgrind) from the tokenize function I've written. Outside of this function, after I assign the tokens to other variables, I call free(tokens) where appropriate, but this doesn't fix the problem. Any help would be hugely appreciated!

Code:

/**
 * Splits user input into an array of tokens.
 **/
char ** tokenize(const char * s, int * n)
{
   /* Sets array of strings and allocates memory, sized appropriately. */
   int i;
   char * token;   
   char ** tokens = malloc((BUF_LEN + EXTRA_SPACES) *sizeof(*token));
   char buf[BUF_LEN];
   strncpy(buf, s, BUF_LEN);

   /* Defines first token by a whitespace. */
   token = strtok(buf, " ");

   i = 0;
   /* While loop defines all consequent tokens also with a whitespace. */
   while (token)
   {
      tokens[i] = malloc((strlen(token)+EXTRA_SPACES) *sizeof(*token));
      strncpy(tokens[i], token, strlen(token));
      i++;
      token = strtok(NULL, " ");
   }
   * n = i;
   return tokens;
}

Solution

  • I added a function to free your array and checked it with valgrind that there is no memory leak.

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <memory.h>
    size_t BUF_LEN = 32;
    int EXTRA_SPACES = 16;
    int length = 0;
    char ** tokenize(const char * s, int * n)
    {
        /* Sets array of strings and allocates memory, sized appropriately. */
        int i;
        char * token;
        char ** tokens = malloc((BUF_LEN + EXTRA_SPACES) *sizeof(*token));
        char buf[BUF_LEN];
        strncpy(buf, s, BUF_LEN);
    
        /* Defines first token by a whitespace. */
        token = strtok(buf, " ");
    
        i = 0;
        /* While loop defines all consequent tokens also with a whitespace. */
        while (token)
        {
            tokens[i] = malloc((strlen(token)+EXTRA_SPACES) *sizeof(*token));
            strncpy(tokens[i], token, strlen(token));
            i++;
            token = strtok(NULL, " ");
            length++;
        }
        * n = i;
        return tokens;
    }
    
    /* deallocates an array of arrays of char*, calling free() on each */
    void free_argv(char **argv, unsigned rows) {
        for (unsigned row = 0; row < rows; row++) {
    
            free(argv[row]);
        }
        free(argv);
    }
    
    int main ()
    {
    int i = 12;
        char **  ch = tokenize("abc", &i);
        free_argv(ch, (unsigned) length);
    
    }
    

    Output

     valgrind ./a.out 
    ==28962== Memcheck, a memory error detector
    ==28962== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==28962== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==28962== Command: ./a.out
    ==28962== 
    ==28962== 
    ==28962== HEAP SUMMARY:
    ==28962==     in use at exit: 0 bytes in 0 blocks
    ==28962==   total heap usage: 2 allocs, 2 frees, 67 bytes allocated
    ==28962== 
    ==28962== All heap blocks were freed -- no leaks are possible
    ==28962== 
    ==28962== For counts of detected and suppressed errors, rerun with: -v
    ==28962== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)