FAM's may be useful in some instances, but the old reliable malloc() (and its user strdup(), if available) can be used here.
realloc() is used to start and to grow array of ptrs to individual 'token' structs
#include <stdio.h>
#include <stdlib.h> // for `exit()`
#include <string.h>
typedef struct {
int length;
char *word; // NB: pointer, not FAM
} TOKENS_t;
typedef struct {
TOKENS_t *a;
int l;
} OUTPUT_t;
#define USE_SRC_STR
// or not defined in order to use dynamic allocation of "word" copies
// return ptr to struct instead of entire struct
OUTPUT_t *explode( char *a, char *b ) {
OUTPUT_t *r = calloc( 1, sizeof *r ); // initially all bytes = 0
// test for NULL return omitted for brevity
// only one invocation of strtok()
for( char *ptr = a; (ptr = strtok( ptr, b )) != NULL; ptr = NULL ) {
// cautiously growing array of pointers
TOKENS_t *tmp = realloc( r->a, (r->l + 1) * sizeof *tmp );
if( tmp == NULL ) {
fprintf( stderr, "realloc failed on %d\n", r->l + 1 );
exit( EXIT_FAILURE );
}
// preserve "word"
#ifdef USE_SRC_STR
tmp[ r->l ].word = ptr;
#else
tmp[ r->l ].word = strdup( ptr );
#endif
tmp[ r->l ].length = strlen( ptr ); // preserve length of "word"
r->a = tmp; // realloc() may have relocated extended array
r->l++; // got one more
}
return r;
}
int main() {
char str[] = "This is test for the start";
OUTPUT_t *r = explode( str, " " ); // capture returned pointer
printf( "%d\n", r->l );
for( int i = 0; i < r->l; i++ )
printf( "%d: %s (%d)\n", i, r->a[i].word, r->a[i].length );
#ifndef USE_SRC_STR
// free those buffers containing copies of "word"s
while( (r->l)-- )
free( r->a[ r->l ].word );
#endif
free( r ); // free this, too!
return 0;
}
Output
6
0: This (4)
1: is (2)
2: test (4)
3: for (3)
4: the (3)
5: start (5)
Or, below is another alternative where the caller passes the address of a 'top level' struct to the function (that now just does its job and returns nothing.)
void explode( OUTPUT_t *r, char *a, char *b ) {
for( char *ptr = a; (ptr = strtok( ptr, b )) != NULL; ptr = NULL ) {
TOKENS_t *tmp = (TOKENS_t *)realloc( r->a, (r->l + 1) * sizeof *tmp );
if( tmp == NULL ) {
fprintf( stderr, "realloc failed on %d\n", r->l + 1 );
exit( EXIT_FAILURE );
}
tmp[ r->l ].word = ptr; // using original string
tmp[ r->l ].length = strlen( ptr );
r->a = tmp;
r->l++;
}
}
int main() {
char str[] = "This is test for the start";
OUTPUT_t r = { 0 }; // define initialised top level instance
explode( &r, str, " " ); // pass into "helper" function
printf( "%d\n", r.l );
for( int i = 0; i < r.l; i++ )
printf( "%d: %s (%d)\n", i, r.a[i].word, r.a[i].length );
free( r.a ); // don't forget to "clean up"
return 0;
}