I tried to create a simple split function to return elements as a struct, and used another struct to return the results and the number of elements.
#include <stdio.h>
#include <stdio.h>
#include <string.h>
struct TOKENS
{
int length;
char word[];
};
struct OUTPUT
{
struct TOKENS *a;
int l;
};
struct OUTPUT explode(char *a, char *b)
{
struct TOKENS tokens[10];
int i = 0;
char *ptr = strtok(a, b);
while (ptr != NULL)
{
tokens[i].length = strlen(ptr);
strcpy(tokens[i].word, ptr);
ptr = strtok(NULL, b);
i ;
}
struct OUTPUT r = {
.a = &tokens[0], .l = i};
return r;
}
int main()
{
char str[] = "This is test for the start";
struct OUTPUT r = explode(str, " ");
struct TOKENS *tokens = r.a;
printf("%d\n", r.l);
for (int i = 0; i < r.l; i )
{
printf("%d %s (%d)\n", i, tokens[i].word, tokens[i].length);
}
return 0;
}
However, the output is not what I aimed for:
6
0 (4)
1 (2)
2 (4)
3 (3)
4 (3)
5 start (5)
CodePudding user response:
As mentioned in the comments, you have to use dynamic allocation when using a structure with a flexible array member. The size of the structure doesn't include the array size, you have to add the size of the array when calling malloc().
This means you can't have an array of TOKENS, so you need OUTPUT.a to be an array of pointers. Use malloc() and realloc() to allocate the memory for r.a, and use malloc() to allocate the memory for each TOKENS element.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct TOKENS
{
int length;
char word[];
};
struct OUTPUT
{
struct TOKENS **a;
int l;
};
struct OUTPUT explode(char *a, char *b)
{
struct OUTPUT r = {.a = NULL, .l = 0};
int i = 0;
char *ptr = strtok(a, b);
while (ptr != NULL)
{
r.l ;
r.a = realloc(r.a, r.l * sizeof(*r.a));
struct TOKENS *token = malloc(sizeof(*token) strlen(ptr) 1);
token->length = strlen(ptr);
strcpy(token->word, ptr);
r.a[i] = token;
ptr = strtok(NULL, b);
i ;
}
return r;
}
int main()
{
char str[] = "This is test for the start";
struct OUTPUT r = explode(str, " ");
struct TOKENS **tokens = r.a;
printf("%d\n", r.l);
for (int i = 0; i < r.l; i )
{
printf("%d %s (%d)\n", i, tokens[i]->word, tokens[i]->length);
}
for (int i = 0; i < r.l; i ) {
free(tokens[i]);
}
free(tokens);
return 0;
}
CodePudding user response:
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 TOKENS {
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;
}
