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;
}