Parad0x13
Parad0x13

Reputation: 2065

Why does free work like this?

Given the following code:

typedef struct Tokens {
    char **data;
    size_t count;
} Tokens;

void freeTokens(Tokens *tokens) {
    int d;
    for(d = 0;d < tokens->count;d++)
        free(tokens->data[d]);
    free(tokens->data);
    free(tokens);
    tokens = NULL;
}

Why do I need that extra:

free(tokens->data);

Shouldn't that be handled in the for loop?

I've tested both against valgrind/drmemory and indeed the top loop correctly deallocates all dynamic memory, however if I remove the identified line I leak memory.

Howcome?

Upvotes: 2

Views: 109

Answers (3)

Purag
Purag

Reputation: 17061

Let's look at a diagram of the memory you're using in the program:

+---------+       +---------+---------+---------+-----+
| data    |  -->  | char *  | char *  | char *  | ... |
+---------+       +---------+---------+---------+-----+
| count   |            |         |         |
+---------+            v         v         v
                     +---+     +---+     +---+
                     | a |     | b |     | c |
                     +---+     +---+     +---+
                     |...|     |...|     |...|
                     +---+     +---+     +---+

In C, we can dynamically allocate space for a group (more simply, an array) of elements. However, we can't use an array type to reference that dynamic allocation, and instead use a pointer type. In this case, the pointer just points to the first element of the dynamically allocated array. If you add 1 to the pointer, you'll get a pointer to the second element of the dynamically allocated array, add two to get a pointer to the second element, and so on.

In C, the bracket syntax (data[1]) is shorthand for addition and dereferencing to a pointer. So pointers in C can be used like arrays in this way.

In the diagram, data pointing to the first char * in the dynamically allocated array, which is elsewhere in memory.

Each member of the array pointed to by data is a string, itself dynamically allocated (since the elements are char *s).

So, the loop deallocates the strings ('a...', 'b...', 'c...', etc), free(tokens->data) deallocates the array data points to, and finally, free(tokens) frees the entire struct.

Upvotes: 6

Code-Apprentice
Code-Apprentice

Reputation: 83527

As a general rule of thumb, every malloc() should have a corresponding call to free(). If you look at the code which allocates the memory in this program, you will very likely see a very strict correspondence with the code you posted here that frees the memory.

Upvotes: 3

Felix Guo
Felix Guo

Reputation: 2708

data is a pointer to a pointer. This means data points to a dynamically allocated array of pointers, which then each point to the actual data. The first for loops frees each of the pointers IN the array, but you still need to free the original pointer TO that array of the other points which you freed already. That's the reason for the line you pointed out.

Upvotes: 4

Related Questions