C - Array of Strings & Mysterious Valgrind Error - c

I'm trying to allocate a two-dimensional array of strings, where the last member is always a NULL pointer, i.e. an empty array consists of a single NULL pointer. I keep getting Valgrind errors but I have no idea why.
/*Initializes the string array to contain the initial
* NULL pointer, but nothing else.
* Returns: pointer to the array of strings that has one element
* (that contains NULL)
*/
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
/* Releases the memory used by the strings.
*/
void free_strings(char **array)
{
int i = 0;
while(array[i] != NULL){
free(array[i]);
i++;
}
//free(array[i]);
free(array);
}
/* Add <string> to the end of array <array>.
* Returns: pointer to the array after the string has been added.
*/
char **add_string(char **array, const char *string)
{
int i = 0;
while(array[i] != NULL){
i++;
}
array = realloc(array, (i+1) * sizeof(char *));
char *a = malloc(strlen(string)+1);
array[i] = malloc(strlen(string)+1);
strcpy(a, string);
strcpy(array[i], a);
free(a);
return array;
}
Here's the Valgrind error:
==375== Invalid read of size 8 ==375==    at 0x402FCE: add_string (strarray.c:40) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518df08 is 0 bytes after a block of size 8 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==375== Invalid read of size 8 ==375==    at 0x4018F7: test_add_string (test_source.c:70) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518e308 is 0 bytes after a block of size 40 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==375== Invalid read of size 8 ==375==    at 0x402F8D: free_strings (strarray.c:25) ==375==    by 0x401AA6: test_add_string (test_source.c:91) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518e308 is 0 bytes after a block of size 40 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==376== Invalid read of size 8 ==376==    at 0x402FCE: add_string (strarray.c:40) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  Address 0x518e6d8 is 0 bytes after a block of size 8 alloc'd ==376==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==376==    by 0x402FF4: add_string (strarray.c:43) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  ==376== Invalid read of size 8 ==376==    at 0x402F8D: free_strings (strarray.c:25) ==376==    by 0x401F5C: test_make_lower (test_source.c:130) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  Address 0x518e9d0 is 0 bytes after a block of size 32 alloc'd ==376==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==376==    by 0x402FF4: add_string (strarray.c:43) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  ==377== Invalid read of size 8 ==377==    at 0x402FCE: add_string (strarray.c:40) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  Address 0x518f3e8 is 0 bytes after a block of size 8 alloc'd ==377==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==377==    by 0x402FF4: add_string (strarray.c:43) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  ==377== Invalid read of size 8 ==377==    at 0x402F8D: free_strings (strarray.c:25) ==377==    by 0x40246A: test_sort_strings (test_source.c:174) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  Address 0x518f6e0 is 0 bytes after a block of size 32 alloc'd ==377==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==377==    by 0x402FF4: add_string (strarray.c:43) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==

That's because you didn't add new NULL-terminator to the array add_string(). So subsequent calls of add_array() fail to find end of array without going out of bounds.
I think you need realloc with larger length:
array = realloc(array, (i + 2) * sizeof(char *));
And then save NULL-terminator to array[i + 1]:
array[i + 1] = NULL;
Why didn't you try using linked lists for that? I feel bad for realloc() per each add_string()

This function
char **add_string(char **array, const char *string)
{
int i = 0;
while(array[i] != NULL){
i++;
}
array = realloc(array, (i+1) * sizeof(char *));
char *a = malloc(strlen(string)+1);
array[i] = malloc(strlen(string)+1);
strcpy(a, string);
strcpy(array[i], a);
free(a);
return array;
}
is wrong. It does not add a new slot into the array and you do not set the last element to NULL.
The valid function can look like
char **add_string( char **array, const char *string )
{
int i = 0;
while ( array[i++] != NULL );
array = realloc( array, ( i + 1 ) * sizeof( char * ) );
array[i] = NULL;
array[i-1] = malloc( strlen( string ) + 1 );
strcpy( array[i-1], string );
return array;
}

Related

String Arrays: Delete original array and then return copy

I am new with C and I am trying to understand allocating strings.
I am trying to create a function called adding_string. It takes in an array of zero or more strings that has a null in the final location. Next, it makes a shallow copy of the array that is + 1 location bigger, then appends a copy of the string str onto the array. Finally, it deletes the original array and returns the new copy
This is what I have so far:
char **adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num);
strncpy(final, str, num);
free(array);
//The above code would create a copy of the string "str".
//Then it puts that into the array.
//Not sure if free(array); would be the right method
//Having issues with returning final too
return final;
}
In the main function, you would have something like:
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *));
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
I'm not sure if free(array) would be the right method to use to delete the original array, and I'm having issues with returning the new copy.
When I try returning the new copy, I get:
warning: return from incompatible pointer type
which is because of:
return final;
Your adding_string makes no sense, you make a copy of str, free the memory
from array and return the new copy. The function should return a double pointer to char,
you are passing a single-pointer to char. All other values are lost, you are
leaking memory like crazy.
I'd rewrite your adding_string like this:
char **adding_string(char **array, const char *str)
{
char **tmp;
if(str == NULL)
return NULL;
// first make copy
size_t len = strlen(str);
char *strcopy = malloc(len+1);
if(strcopy == NULL)
return NULL;
// you've allocated enough memory for the copy
// no need of strncpy here
strcpy(strcopy, str);
// get the number of strings saved
size_t size = 0; // number of strings saved
if(array)
{
tmp = array;
while(*(tmp++))
size++;
}
// reallocate memory for array of strings
tmp = realloc(array, (size+2) * sizeof *tmp);
if(tmp == NULL)
{
// something went wrong, free the copy
free(strcopy);
return NULL;
}
tmp[size] = strcopy;
tmp[size+1] = NULL;
return tmp;
}
Note that in this version, if array is NULL, the function allocates the memory for the
array of strings. That's only a design choice, you could as well check that
array is not NULL and pass to adding_string a pre-allocated array of
strings. I think (and that's only my opinion) that is more elegant that
adding_string will create the first array. In this way, the code that
allocates memory is in one place only.
Now in your main
char **array = NULL;
char **lines;
// adding_string will allocate the memory for array when it's NULL
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
Note that I do
tmp = realloc(array, (size+2) * sizeof *tmp);
size has the number of strings saved, that means that array
holds size+1 spaces, because the last one points to NULL. You are appending
one more strings, so you have to reallocate size+1+1 spaces, which is
size+2.
Please don't forget to free the memory afterwards.
The program below strictly follows your needs and intentions.
The array array is resized every time a new string is added. At the end of the program the proper cleanup of all allocated memory is done.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final = (char *)malloc(num); // allocate memory for the string `str`
strncpy(final, str, num); // create the copy of the `str`
int i=0;
for(i=0; array[i] !=NULL; i++) {} // find how many elements do we have in the array
array[i] = final; // add final to the first empty spot in the `array`
i++;
char ** new_array = calloc(1+i, sizeof(char *)); // allocate a new array 1 size bigger
memcpy(new_array, array, sizeof(char*)*i); // copy all the pointers
free (array); // no need for the old array
return new_array; // return a pointer to the new bigger array
}
int main(void)
{
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *)); // allocate array for 4 poiters if type (char *)
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
free(*lines);
}
free (array);
return 0;
}
Output:
help
plz
thanks
This is different approach where
char *adding_string(const char *str)
returns a pointer (char *) to the copy of the string. The array has already preallocated memory to accommodate all string pointers.
A small program to demonstrate the concept:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *adding_string(const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num); // allocate memory for the string str
strncpy(final, str, num); // crreate the copy
return final; // return a pointer to created copy
}
int main(void)
{
char **array = NULL;
array = (char **)calloc(4, sizeof(char *)); // allocate array for 4 pointers if type (char *)
array[0] = adding_string("help");
array[1] = adding_string("plz");
array[2] = adding_string("thanks");
for (int i=0; i<3; i++ )
{
printf("%s\n", array[i]);
free(array[i]);
}
free (array);
return 0;
}
Output:
help
plz
thanks

Adding string to array, pointer being realloc'd was not allocated

I am trying implement a method that adds a given string to an array that ends with a NULL pointer. This is what I have so far but I am getting an error saying that the pointer being realloc'd was not allocated.
int main(void)
{
char **strings = init_array();
strings = add_string(strings, "one");
strings = add_string(strings, "two");
return 1;
}
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
char **add_string(char **array, const char *string)
{
unsigned int size = 0;
while (*array) {
size++;
array++;
}
char **newarr = (char **)realloc(array, sizeof(char *) * (size + 2));
newarr[size] = malloc(strlen(string)+1);
strcpy(newarr[size], string);
newarr[size+1] = NULL;
return newarr;
}
The issue is array++. You have to pass realloc the same value malloc returned (your array argument), but you modify it during the loop, so it'll work only the first time (because *array will immediately false). You could use:
size_t size;
for(size = 0; array[size]; size++);
And leave the rest untouched.
In your while (*array) loop you are incrementing not only the size, but also the array pointer itself. As a result, at the end of the loop size contains the length of the array, and the array pointer points to the last (NULL) element. This pointer was never allocated, (it points within an allocated block,) therefore it is not a valid pointer to reallocate. (And definitely that's not what you intended to do.)
So, just don't do array++ within that loop.
Your loop that calculates the number of strings in the array also advances the variable itself. You could use a temporary variable instead:
char **temp = array;
while (*temp)
...
Or separate the counting into a function.
BTW you don't need a casting when using realloc, for the same reason you don't do the casting with malloc. This is not a bug, but it better be consistent.
Summarizing all other answers given so far, adding some best practise tweaks, the relevant code should look like this:
char **add_string(char **array, const char *string)
{
char ** newarr;
size_t size = 0;
assert (NULL != string); /* Need to include assert.h */
if (NULL != array)
{
while (NULL != array[size])
{
++size; /* Just count, do not touch the pointer value allocated. */
}
}
newarr = realloc(array, (size + 2) * sizeof *newarr);
if (NULL == newarr) /* Test the outcome of reallocation. */
{
perror("realloc() failed"); /* Need to include stdio.h */
return NULL;
}
newarr[size] = malloc(strlen(string) + 1);
if (NULL == newarr[size])
{
perror("malloc() failed"); /* Need to include stdio.h */
/* Might want to clean up here and indicate the failure to the
caller by returning NULL. */
}
else
{
strcpy(newarr[size], string);
}
newarr[size+1] = NULL;
return newarr;
}
Or even tighter:
char **add_string(char **array, const char *string)
{
assert (NULL != string); /* Need to include assert.h */
{
size_t size = 0;
if (NULL != array)
{
while (NULL != array[size])
{
++size; /* Just count, do not touch the pointer value allocated. */
}
}
{
char ** newarr = realloc(array, (size + 2) * sizeof *newarr);
if (NULL == newarr)
{
perror("realloc() failed"); /* Need to include stdio.h */
}
if (NULL != newarr)
{
newarr[size] = malloc(strlen(string) + 1);
if (NULL == newarr[size])
{
perror("malloc() failed"); /* Need to include stdio.h */
}
else
{
strcpy(newarr[size], string);
}
newarr[size+1] = NULL;
}
return newarr;
}
}
}
The easiest way would be to preserve initial array pointer and use it to realloc memory.
int main(void)
{
char **strings = init_array();
strings = add_string(strings, "one");
strings = add_string(strings, "two");
return 1;
}
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
char **add_string(char **array, const char *string)
{
char** cache = array;
unsigned int size = 0;
while (*array) {
size++;
array++;
}
char **newarr = (char **)realloc(cache, sizeof(char *) * (size + 2));
newarr[size] = malloc(strlen(string)+1);
strcpy(newarr[size], string);
newarr[size+1] = NULL;
return newarr;
}
Another note - main function should return 0 on success.

Dynamic array of pointers

I have an array of pointers to strings.
char **array;
I declare it this way and not char *array[N] because this array won't have a static number of elements.
Declaring the array this way, I will probably have to realloc the sizeof it every time I add a new element (pointer to string).
int main(void)
{
char **array;
char *word = "lolol";
char *word2 = "blabla";
return 0;
}
Could you give me an example on how I should "create space" in the array in order to store pointers to these strings?
The best way of doing it is probably by making a struct
This way, you can resize it, and add as many strings as you want without needing to choose a specific size.
Note: setting the string_array's capacity and size to 0 is necessary for it to work.
You could do it by a function like this instead:
void load_array(string_array *array)
{
array->size = 0;
array->capacity = 0;
}
And call it like this:
load_array(&my_array);
Note, when getting the value from one of these arrays using [], you must call it like this:
my_array.arr[index]
This is because you must refer to the pointer in the array struct, which is as arr (char **)
I have tested the below, and it works perfectly.
# include <stdio.h>
typedef struct string_array
{
char **arr;
unsigned capacity, size;
} string_array;
void add_to_array(string_array *array, char *str)
{
if(array->capacity == 0)
{
array->arr = (char **)malloc((array->capacity = 3) * sizeof(char *));
array->arr[array->size++] = str;
}
else if(array->capacity == array->size)
{
array->arr = (char **)realloc(array->arr, (array->capacity *= 1.5) * sizeof(char *));
array->arr[array->size++] = str;
}
else
{
array->arr[array->size++] = str;
}
}
int main(void)
{
char *str1 = "Hello World";
char *str2 = "Hello World2";
char *str3 = "Hello World3";
char *str4 = "Hello World4";
char *str5 = "Hello World5";
string_array my_array;
my_array.capacity = 0;
my_array.size = 0;
add_to_array(&my_array, str1);
add_to_array(&my_array, str2);
add_to_array(&my_array, str3);
add_to_array(&my_array, str4);
add_to_array(&my_array, str5);
// and so on
for (int i = 0; i < my_array.size; ++i)
{
printf(my_array.arr[i]);
printf("\n");
}
free(my_array.arr);
getchar(); // this means pressing enter closes the console
return (0);
}
Here is a demonstrative program
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t N = 2;
char **array = malloc( N * sizeof( char * ) );
if ( !array ) return 1;
char *word = "lolol";
char *word2 = "blabla";
array[0] = word;
array[1] = word2;
char *word3 = "Hello";
++N;
array = realloc( array, N * sizeof( char * ) );
if ( !array ) return 2;
array[2] = word3;
for ( size_t i = 0; i < N; i++ ) puts( array[i] );
free( array );
return 0;
}
The program output is
lolol
blabla
Hello
Simply allocate some room for pointers in your array, and free it when you're done.
char *word = "lolol";
char *word2 = "blabla";
char** array = malloc(2*sizeof(char*));
array[0] = word;
array[1] = word2;
free(array);
You can change that 2*sizeof(char*) to a N*sizeof(char*) if you want more elements.

How to pass a double pointer to a function without segmentation fault C language

I'm trying to pass a double pointer as an argument to a function and I can't see why the segmentation fault happen...
Here is the function:
void create_path_list(char *path_, char ***path) {
// Convert the path (string) into a list of directories
char *token = NULL;
int i = 0;
*path = (char **) realloc(*path, (i + 1) * sizeof(char *));
(*path)[i] = (char *) malloc(2);
strcpy((*path)[0], "/");
for(token = strtok(path_,"/"), i = 1; token != NULL; token = strtok(NULL, "/"), ++i)
{
*path = (char **) realloc(*path, (i + 1) * sizeof(char *));
(*path)[i] = (char *) malloc(sizeof(token) + 1);
strcpy((*path)[i], token);
}
}
Here is the main:
int main(){
char **path = NULL;
create_path_list("/dir1/dir2/dir3/file.txt", &path);
return 0;
}
sizeof(token)
Will give the size of token, which is a pointer. That will not allocate enough space to copy for the entire string
malloc(sizeof(token) + 1);
strcpy((*path)[i], token);
You should replace sizeof with a strlen
You are passing a string literal to you function and then try to change it with strtok(). You will have to pass a mutable string.
char str[] = "/dir1/dir2/dir3/file.txt" ;
create_path_list( str , &path);
Also I don't see how can you know how large is your allocated array if pointers. You will have to either return the size or NULL terminate the array.
Set the last element to null:
*path = (char **) realloc(*path, (i + 1) * sizeof(char *));
(*path)[i] = NULL ;
And print it outside the function
for( size_t i = 0 ; path[i] ; i++ )
{
printf("%s" , path[i] ) ;
}

segmentation fault inside on loop

I'm C# man, new in C language working with points first time.
I have this function that works with malloc(), realloc() and free() at future:
char ** split(char * delimiter, char * input) {
int i = 0;
int size = sizeof(char *);
char ** tokens;
char * token;
char * state;
tokens = (char **) malloc(size);
if(tokens == NULL) {
printf("Allocation failed.");
return;
}
for(token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state),
i++, size *= i) {
tokens = (char **) realloc(tokens, size);
if(tokens == NULL) {
printf("Realloc failed.");
return;
}
tokens[i] = state;
}
return tokens;
}
when I call:
char * IPNumber = "127.0.01";
char * delimiter = ".";
char ** parts = split(delimiter, IPNumber);
it gives segmentation fault.
I'm looking for an explanation how to get(calculate) the size value to be used in the second argument of realloc() function. Thanks in advance.
Ok, I guessed what you intended was to return an array of strings:
include
char ** split(char * delimiter, char * input) {
int i;
char ** tokens;
char * token;
char * state;
tokens = (char **) malloc(sizeof(char *) * (2));
if(tokens == NULL) {
printf("Allocation failed.");
return NULL;
}
tokens[0]=(char *)1; /* one element populated */
tokens[1]=NULL; /* no tokens */
for(i=1, token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state),
i++) {
/* grow array by one element - originally made with 2 */
{
char **new =(char **) realloc(tokens, (i+2) * sizeof(char *));
if(new == NULL) {
printf("Realloc failed.");
free(tokens);
return NULL;
}
else
{
tokens = new;
tokens[i+1] = NULL; /* initialize new entry */
}
}
tokens[i] = token;
tokens[0] = (char *)i;
}
return tokens;
}
int main( void )
{
char str[] = "129.128.0.1";
char delim[] = ".";
char **ret;
ret = split( delim, str );
printf( "tokens = %d\n", (int)ret[0] );
printf( "tokens[1] = %s\n", ret[1] );
printf( "tokens[2] = %s\n", ret[2] );
printf( "tokens[3] = %s\n", ret[3] );
printf( "tokens[4] = %s\n", ret[4] );
printf( "tokens[5] = %s\n", ret[5] );
}
return explicit values, not garbage.
change in realloc function. You grow the array by one element during each loop.
Fix memory leak
save the value returned by strtok_r, not its private internal state variable.
the array is one larger then it needs to be, so make sure it gets initialized to NULL
entry zero of the array is the size, which should not overflow unless you are handling HUGE strings
The sizes of your malloc/calloc are wrong (you multiply by the intended count, which makes the array grow by count!)
On the first item: i=0, size=sizeof(char*);
On the second item i=1, size=sizeof(char) /*that is too small for two elements */
char ** split(char * delimiter, char * input) {
unsigned size , used;
char ** array = NULL;
char * token;
char * state;
size = used = 0;
for(token=strtok_r(input, delimiter, &state); token; token=strtok_r(NULL, delimiter, &state) ) {
if (used+1 >= size) {
size = size ? 2*size: 4;
array = realloc(array, size * sizeof *array);
if (!array) { printf("Realloc failed."); return NULL ; /*leak here*/ }
}
array[used++] = state;
}
/* NOTE: need a way to communicate the number of elements back to the caller */
if (array) array[used] = NULL;
return array;
}
UPDATE: here is a test driver
int main(void)
{
char stuff[] = "this is the stuff";
char **ppp;
unsigned idx;
ppp = split( " " , stuff);
for (idx = 0; ppp && ppp[idx]; idx++) {
fprintf(stdout, "%u: %s\n", idx, ppp[idx] );
}
return 0;
}
Complete rewrite. There are some issues with the original code as posted.
The reallocation size computation is incorrect.
The passing of a string constant to strtok_r is not valid. It modifies the first argument, so that could result in an access violation when passed the string literal.
The assignment of the token into the result array starts at position 1 instead of 0.
The assignment uses the state variable instead of the token (probably not at all the desired result and probably undefined behavior).
There is no way for the caller to know how many tokens are in the returned array.
A failed call to realloc does not free the original pointer, so it would leak.
So rather than attempt to describe the changes, I'll follow the same pattern as others and show what might be a cleaner implementation with a single allocation based on the max possible number of tokens.
char ** split(char * delimiter, char * input) {
int size;
int maxsize;
char ** tokens;
char * token;
char * state;
// compute max possible tokens, which is half the input length.
// Add 1 for the case of odd strlen result and another +1 for
// a NULL entry on the end
maxsize = strlen( input ) / 2 + 2;
tokens = (char**)malloc( maxsize * sizeof( char*) );
if(tokens == NULL) {
printf("Allocation failed.");
return NULL;
}
size = 0;
for(token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state) ) {
tokens[size++] = token;
}
assert( size < maxsize );
// Put a NULL in the last entry so the caller knows how many entries
// otherwise some integer value would need to be returned as an output
// parameter.
tokens[size] = NULL;
// NOTE: could use realloc from maxsize down to size if desired
return tokens;
}
Usage might look like the following. Note the use of strdup to avoid passing the string constant to the function:
char * IPNumber = strdup( "127.0.01" );
char * delimiter = ".";
char ** parts = split(delimiter, IPNumber);
int i;
if ( parts ) {
for ( i = 0; parts[i] != NULL; i++ )
printf( "%s\n", parts[i] );
free( parts );
}
free( IPNumber );
I was going to point out things to fix, but instead just rewrote it as follows:
char **split(char *delim, char *input)
{
char *save; /* saved state for strtok_r */
char **tmp, /* temporary result from realloc (for error handling) */
**res; /* result - NULL-terminated array of tokens */
int i, /* index of current/last token */
count; /* number of elements in res (including NULL) */
/* Allocate first element for res */
if ( !(res = malloc(sizeof(res[0]))) ) {
/* return NULL if malloc() fails */
fprintf(stderr,"split(): malloc() failed\n");
return NULL;
}
/* res[0] = first token, or NULL */
res[0] = strtok_r(input,delim,&save);
/* if it was a token, grab the rest. Last one will be the NULL
* returned from strtok_r() */
if (res[0])
i = 0;
count = 1;
do {
/* Resize res, for next token */
/* use a temporary pointer for realloc()'s result, so that
* we can check for failure without losing the old pointer */
if ( tmp = realloc(res, sizeof(res[0]) * ++count) )
res = tmp;
else {
/* if realloc() fails, free res and return NULL */
free(res);
fprintf(stderr,"split(): realloc() failed.\n");
return NULL;
}
/* get next token, or NULL */
res[++i] = strtok_r(NULL,delim,&save);
} while (res[i]); /* done when last item was NULL */
return res;
}
So the size for realloc is the number of elements needed, multiplied by the size of an element.
The above version of the code returns a NULL-terminated array. Another approach would be to return the number of array elements somehow (like via an int * or size_t * argument); but in any case you need a way for the caller to know where the end of the results array is.
Using strtok_r() for this also adds another catch: The original input string is not left intact. So you'll need to bear that in mind when using this (or your original) function as well -- either use it when you don't need to preserve the original string, or make a duplicate of the original first.

Resources