You ask:
Is there anything I should know about
using strtok on a malloced string?
There are a number of things to be aware of. First, strtok()
modifies the string as it processes it, inserting nulls ('\0'
) where the delimiters are found. This is not a problem with allocated memory (that's modifiable!); it is a problem if you try passing a constant string to strtok()
.
Second, you must have as many calls to free()
as you do to malloc()
and calloc()
(but realloc()
can mess with the counting).
In my code I have (in general terms)
char* line=getline();
Parse(dest,line);
free(line);
Unless Parse()
allocates the space it keeps, you cannot use the dest
structure (or, more precisely, the pointers into the line within the dest
structure) after the call to free()
. The free()
releases the space that was allocated by getline()
and any use of the pointers after that yields undefined behaviour. Note that undefined behaviour includes the option of 'appearing to work, but only by coincidence'.
where getline() is a function that
returns a char * to some malloced
memory, and Parse(dest, line) is a
function that does parsing online,
storing the results in dest (which
has been partially filled earlier,
from other information).
Parse() calls strtok() a a variable
number of times on line, and does some
validation. Each token (a pointer to
what is returned by strtok()) is put
into a queue 'til I know how many I
have.
Note that the pointers returned by strtok()
are all pointers into the single chunk of space allocated by getline()
. You have not described any extra memory allocation.
They are then copied onto a malloc'd
char** in dest.
This sounds as if you copy the pointers from strtok()
into an array of pointers, but you do not attend to copying the data that those pointers are pointing at.
Now free(line) and a function that
free's each part of the char*[] in
dest,
both come up on valgrind as:
"Address 0x5179450 is 8 bytes inside a block of size 38 free'd"
or something similar.
The first free()
of the 'char *[]
' part of dest
probably has a pointer to line
and therefore frees the whole block of memory. All the subsequent frees on the parts of dest
are trying to free an address not returned by malloc()
, and valgrind
is trying to tell you that. The free(line)
operation then fails because the first free()
of the pointers in dest
already freed that space.
I'm considering refactoring my code
[to] store a copy of them [...].
The refactoring proposed is probably sensible; the function strdup()
already mentioned by others will do the job neatly and reliably.
Note that after refactoring, you will still need to release line, but you will not release any of the pointers returned by strtok()
. They are just pointers into the space managed by (identified by) line
and will all be released when you release line
.
Note that you will need to free each of the separately allocated (strdup()
'd) strings as well as the array of character pointers that are accessed via dest
.
Alternatively, do not free line immediately after calling Parse()
. Have dest
record the allocated pointer (line
) and free that when it frees the array of pointers. You still do not release the pointers returned by strtok()
, though.