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.
Related
I am using the below function to replace a sub-string in a given string
void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *result = NULL;
int i, cnt = 0;
int tolen = strlen(to);
int fromlen = strlen(from);
if (*inputString == NULL)
return;
// Counting the number of times old word
// occur in the string
for (i = 0; (*inputString)[i] != '\0'; i++)
{
if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
{
cnt++;
// Jumping to index after the old word.
i += fromlen - 1;
}
}
// Making new string of enough length
result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return;
memset(result, 0, i + cnt * (tolen - fromlen) + 1);
i = 0;
while (&(*inputString))
{
// compare the substring with the result
if (strstr(*inputString, from) == *inputString)
{
strncpy(&result[i], to, strlen(to));
i += tolen;
*inputString += fromlen;
}
else
{
result[i++] = (*inputString)[0];
if ((*inputString)[1] == '\0')
break;
*inputString += 1;
}
}
result[i] = '\0';
*inputString = result;
return;
}
The problem with the above function is memory leak. Whatever memory is allocated for inputString will be lost after this line.
*inputString = result;
since I am using strstr and moving pointer of inputString *inputString += fromlen; inputString is pointing to NULL before the above line. So how to handle memory leak here.
Note: I dont want to return the new memory allocated inside the function. I need to alter the inputString memory based on new length.
You should use a local variable to iterate over the input string and avoid modifying *inputString before the final step where you free the previous string and replace it with the newly allocated pointer.
With the current API, ReplaceSubStr must be called with the address of a pointer to a block allocated with malloc() or similar. Passing a pointer to local storage or a string literal will have undefined behavior.
Here are a few ideas for improvement:
you could return the new string and leave it to the caller to free the previous one. In this case, you would take the input string by value instead of by address:
char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
If the from string is empty, you should either insert the to string between each character of the input string or do nothing. As posted, your code has undefined behavior for this border case.
To check if the from string is present at offset i, use memcmp instead of strstr.
If cnt is 0, there is nothing to do.
You should return an error status for the caller to determine if memory could be allocated or not.
There is no need to initialize the result array.
avoid using strncpy(). This function has counter-intuitive semantics and is very often misused. Read this: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ReplaceSubStr(char **inputString, const char *from, const char *to) {
char *input = *inputString;
char *p, *q, *result;
size_t cnt;
size_t tolen = strlen(to);
size_t fromlen = strlen(from);
if (input == NULL || fromlen == 0)
return 0;
// Counting the number of times old word occurs in the string
for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
p += fromlen;
}
if (cnt == 0) // no occurrence, nothing to do.
return 0;
// Making new string of enough length
result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return -1;
for (p = input, q = result;;) {
char *p0 = p;
p = strstr(p, from);
if (p == NULL) {
strcpy(q, p0);
break;
}
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, to, tolen);
q += tolen;
p += fromlen;
}
free(*inputString);
*inputString = result;
return 0;
}
int main() {
char *p = strdup("Hello world!");
ReplaceSubStr(&p, "l", "");
printf("%s\n", p); // prints Heo word!
free(p);
return 0;
}
You cannot obviously free the input as it can be a literal, some memory you don't control. That would cripple your function even more than now.
You could return the old value of inputString so you'd be able to free it if needed.
char *ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *old_string = *inputString;
...
return old_string;
}
The caller is responsible to free the contents of old_string if needed.
If not needed (we have to workaround the char ** input by assigning a valid writable array to a pointer to be able to pass this pointer:
char input[]="hello world";
char *ptr = input;
ReplaceSubStr(&ptr, "hello", "hi");
// input is now "hi world" in a different location
free(ptr); // when replaced string isn't needed
if needed:
char *input = strdup("hello world");
char *old_input = ReplaceSubStr(&input, "hello", "hi");
free(old_input);
or just
free(ReplaceSubStr(&input, "hello", "hi"));
then always (when replaced string isn't needed):
free(input);
The only constraint is that you cannot use a constant string literal as input (const char *input = "hello world") because of the prototype & the possible return of a char * to pass to free.
So, here's my logic:
This is some text:
char *text;
Then this is array of texts:
char **arr;
Then array of these arrays is:
char ***arr2d;
And if I want a function to modify it, it needs to accept it as:
char ****arr2d;
And within the function use it as:
*arr2d = (e.g. allocate);
So if I want to create 2D array like this and make the first row, first column contain just a letter 'a', then why does this not work?
#define COLUMNS 7
void loadTable(char ****table)
{
*table = (char ***) malloc(sizeof(char**));
if (!*table) {
printf("Allocation error for table rows.");
return;
}
*table[0] = (char**) malloc(COLUMNS * sizeof(char*));
if (!*table[0]) {
printf("Allocation error for table columns.");
return;
}
*table[0][0] = (char*) malloc(2 * sizeof(char));
*table[0][0][0] = (char)(97);
*table[0][0][1] = '\0';
}
int main()
{
char ***table;
loadTable(&table);
return 0;
}
You would need only 3 *** to do what you describe, not 4 ****. Be aware, there are methods to allow you to avoid excessive depth in terms of arrays of arrays of strings. And there are also good reasons to avoid excessively deep orders of arrays, not the least is the need to free(.) everything you allocate. That means exactly one call to free(.) for each and every call to [m][c][re]alloc(.).
But to address your question...
In general, to create new memory for a single array of a previously allocated memory, you could use a function prototyped as:
char * ReSizeBuffer(char **str, size_t origSize);
Where if say the previously allocated buffer were created as:
char *buf = calloc(origSize, 1);
...the usage could look like:
char *tmp = {0};
tmp = ReSizeBuffer(&buf, newSize); //see implementation below
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Then if this works for a single array of char, then the prototype for allocating new memory for a previously allocated array of strings might look like this:
char ** ReSizeBuffer(char ***str, size_t numArrays, size_t strLens);
Where if say the previously allocated 2D buffer were created as:
char **buf = Create2DStr(size_t numStrings, size_t maxStrLen); //see implementation below
...the usage could look like:
char **tmp = {0};
tmp = ReSizeBuffer(&buf, numStrings, maxStrLen);
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Implementations:
Implementation of ReSizeBuffer. This must be expanded if you desire to implement the second prototype:
char * ReSizeBuffer(char **str, size_t size)
{
char *tmp={0};
if(!(*str)) return NULL;
if(size == 0)
{
free(*str);
return NULL;
}
tmp = (char *)realloc((char *)(*str), size);
if(!tmp)
{
free(*str);
return NULL;
}
*str = tmp;
return *str;
}
Implementation of Create2DStr might look like this:
char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
I just stumbled upon this old question of mine and spotted the problem immediately. Here is the answer:
The number of asterisks is actually correct, the problem is operator evaluation. Specifically, all lines of code of this form:
*table[0]
should have been written as:
(*table)[0]
I am new to C and I want to create a dynamic array to store strings. I wrote the code below for it but it didn't work. Array elements contain some ASCII chars instead of string.
I want historyArray[0] value to be "foo". How can I do that?
typedef struct {
char *historyCommand;
int usedSize;
int maximumSize;
} HistoryArray;
void CreateHistoryArray(HistoryArray *HistoryArray) {
HistoryArray->historyCommand = (char *) malloc(sizeof(char) * MAX_LEN);
HistoryArray->usedSize = 0;
HistoryArray->maximumSize = INITIAL_SIZE;
}
void ExpandHistoryArray(HistoryArray *HistoryArray, int newSize) {
int *newArray = (char *) malloc(sizeof(char) * newSize);
memcpy(newArray, HistoryArray->historyCommand, sizeof(char) * HistoryArray->maximumSize);
free(HistoryArray->historyCommand);
HistoryArray->historyCommand = newArray;
HistoryArray->maximumSize = newSize;
}
void AddHistoryValue(HistoryArray *HistoryArray, char historyCommand[]) {
strcpy(HistoryArray->historyCommand[HistoryArray->usedSize], historyCommand);
HistoryArray->usedSize++;
if (HistoryArray->usedSize == HistoryArray->maximumSize) {
ExpandHistoryArray(HistoryArray, HistoryArray->maximumSize * 2);
}
}
void freeHistoryArray(HistoryArray *a) {
free(a->historyCommand);
a->historyCommand = NULL;
a->usedSize = 0;
a->maximumSize = 2;
}
HistoryArray historyArray;
There are a number of problems in your code.
char *historyCommand is a pointer to a single string, not an array of strings. For a pointer to an array of strings you should use char **historyCommand.
You don't need to allocate the space for the individual strings when you create the HistoryArray. You can allocate the proper amount of space each time you add to the array, using the length of the string you're adding.
You should use realloc() instead of calling malloc(), memcpy(), and free(). This has the benefit that sometimes it can simply expand the memory it already allocated, so no copying will be needed.
When you're freeing the HistoryArray, you need to free all the strings. You shouldn't free historyCommand, because you set maximumSize = 2, and the other functions assume that this means there's room for 2 items there, which isn't true if you set historyCommand to NULL. So you should resize it to maximumSize to be consistent with the rest of the code.
Here's the new code:
typedef struct {
char **historyCommand;
int usedSize;
int maximumSize;
} HistoryArray;
void CreateHistoryArray(HistoryArray *HistoryArray) {
HistoryArray->historyCommand = malloc(INITIAL_SIZE * sizeof(char *));
HistoryArray->usedSize = 0;
HistoryArray->maximumSize = INITIAL_SIZE;
}
void ExpandHistoryArray(HistoryArray *HistoryArray, int newSize) {
HistoryArray->historyCommand = realloc(HistoryArray->historyCommand, newSize * sizeof(char *));
HistoryArray->maximumSize = newSize;
}
void AddHistoryValue(HistoryArray *HistoryArray, char historyCommand[]) {
historyCommand[HistoryArray->usedSize] = malloc(strlen(historyCommand) + 1);
strcpy(HistoryArray->historyCommand[HistoryArray->usedSize], historyCommand);
HistoryArray->usedSize++;
if (HistoryArray->usedSize == HistoryArray->maximumSize) {
ExpandHistoryArray(HistoryArray, HistoryArray->maximumSize * 2);
}
}
void freeHistoryArray(HistoryArray *a) {
for (int i = 0; i < a->usedSize; i++) {
free a->historyCommand[i];
}
a->usedSize = 0;
a->maximumSize = 2;
a->historyCommand = realloc(a->historyCommand, a->maximumSize * sizeof(char *));
}
HistoryArray historyArray;
A way of ending a char array before it becomes full is to put '\0' at end, like-
single_str[5] ='\0';
Then how to end a 2D char array in that way?
In practice, you should in C avoid thinking of 2D arrays. Stricto sensu, the C language don't know about 2D arrays, only about arrays of arrays (fixed length, all of the same size) or about arrays of pointers (or array of aggregates or scalars).
You might use an array of string pointers. C strings are conventionally ended by a zero byte. Here is an example of constant array (of constant strings, i.e. const char* pointers) ended by a NULL string
const char*const arrstr[] = {
"Hello",
"Nice",
"World",
NULL
};
On my machine, sizeof(const char*) is 8, so sizeof(arrstr) is 32; and sizeof("Nice") is 5.
You could print all the array members with
for (const char*const* p = arrstr; *p; p++) printf("%s\n", *p);
You could use in C99 a structure ending with a flexible array member, e.g.
struct my_string_array_st {
unsigned len;
char* arr[]; // array of len pointers */
};
then you might have a constructor function which build such string arrays with empty content like
struct my_string_array_st* make_string_array(unsigned ln) {
struct my_string_array_st*p
= malloc(sizeof(struct my_string_array_st) + len*sizeof(char*));
if (!p) { perror("malloc"); exit(EXIT_FAILURE); };
p->len = ln;
for (unsigned ix=0; ix<ln; ix+) p->arr[ix] = NULL;
return p; }
you'll then decide (this is your convention to follow) that each string inside is heap-allocated with strdup - so you don't have two aliased pointers inside. Here is the function to set a string (and release the previous one, if needed)
void
set_string_array(struct my_string_array_st*p, unsigned ix, const char*str) {
if (!p || ix>=p->len) return;
free(p->arr[ix]);
char* s = NULL;
if (str) {
s = strdup(str);
if (!s) { perror("strdup"); exit(EXIT_FAILURE); };
};
p->arr[ix] = s;
}
(recall that you are allowed to free(3) a NULL pointer; it is a no-op)
and here is a destructor function, releasing all the internal strings.
void destroy_string_array(struct my_string_array_st*p) {
if (!p) return;
unsigned l = p->len;
for (unsigned ix=0; ix<l; ix++) free(p->arr[ix]);
free (p);
}
Of course, here is an accessor function:
const char* nth_string_array(struct my_string_array_st*p, unsigned ix)
{
if (!p || ix>=p->len) return NULL;
return p->arr[ix];
}
You can use an empty string "":
#include <stdio.h>
int main(void)
{
char arr[][3] = {"ab", "cd", ""};
char (*p)[3] = arr;
while (**p) { /* while not an empty string */
printf("%s\n", *p);
p++;
}
return 0;
}
If arr can contain an empty string, you can use a non printable character, like ETX (end of text):
#include <stdio.h>
int main(void)
{
char arr[][3] = {"ab", "", "\x03"};
char (*p)[3] = arr;
while (**p != '\x03') {
printf("%s\n", *p);
p++;
}
return 0;
}
For a a non modifiable array you can use NULL:
#include <stdio.h>
int main(void)
{
char *arr[] = {"ab", "cd", NULL}; /* Read only */
char **p = arr;
while (*p) { /* while not NULL */
printf("%s\n", *p);
p++;
}
return 0;
}
You need to define a sentinel value.
As sentinel for linear and scattered arrays this can be any valid C-"string", which is known to not be used as play-load value during operation for any of the array's elements.
For scattered arrays also NULL can be used as sentinel.
Example for linear array
Define the sentinel value:
#define END_OF_ARRAY "__EOA__"
or just the empty "string":
#define END_OF_ARRAY ""
Note that the sentinel has to be a C-"string" when used with a linear array!
char array [][8] = {
"1st",
"2nd",
END_OF_ARRAY
}
To detect the array's size you then might use a function like this:
ssize_t get_array_size(const char (*parray)[][8])
{
ssize_t s = -1;
if (NULL == parray)
{
errno = EINVAL;
}
else
{
s = 0;
while (strcmp((*parray)[s], END_OF_ARRAY))
{
++s;
}
}
return s;
}
Example for scattered array
Define the sentinel value:
#define END_OF_ARRAY NULL
or also possible
#define END_OF_ARRAY "__EOA__"
Note that commonly NULL is used.
char * array[] = {
"1st",
"2nd",
END_OF_ARRAY
}
To detect the array's size you then might use a function like this:
ssize_t get_array_size(const char *** parray)
{
ssize_t s = -1;
if (NULL == parray || NULL == *parray)
{
errno = EINVAL;
}
else
{
s = 0;
while ((*parray)[s] != END_OF_ARRAY)
{
++s;
}
}
return s;
}
Usage
For both examples call it like this:
int main(void)
{
ssize_t result = get_array_size(&array);
if (-1 == result)
{
perror("get_array_size() failed");
}
else
{
size_t size = result;
printf("number of elements: %zu\n", size)
}
return 0;
}
Simpler approach for scattered arrays
Define the sentinel value:
#define END_OF_ARRAY NULL
or also possible
#define END_OF_ARRAY "__EOA__"
Note that commonly NULL is used.
char * array[] = {
"1st",
"2nd",
END_OF_ARRAY
}
To detect the array's size you then might use a function like this:
ssize_t get_array_size(const char ** parray)
{
ssize_t s = -1;
if (NULL == parray)
{
errno = EINVAL;
}
else
{
s = 0;
while (parray[s] != END_OF_ARRAY)
{
++s;
}
}
return s;
}
Usage
Note when calling this simpler implementation the array is passed directly and not its address is being passed:
int main(void)
{
ssize_t result = get_array_size(array);
if (-1 == result)
{
perror("get_array_size() failed");
}
else
{
size_t size = result;
printf("number of elements: %zu\n", size)
}
return 0;
}
You can assign like this.
char ar[3][20];
ar[2][0]='\0';
You can place the null at which position you need. Consider you need to place the null in 1th line 10th column you can do like this.
ar[1][10]='\0';
Like this where you need to place the null you can place the null.
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.