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.
Related
I am trying to make a function that receives a dynamic string and removes from it all occurrences of the character also passed as a parameter.
The string should finally contain just enough space to contain characters not deleted
void delete(char *cad, char c){
int i, cont = 0;
char *aux = NULL;
i = 0;
while(cad[i] != '\0'){
if(cad[i] != c){
aux = (char*)realloc(aux, sizeof(char) * cont + 1);
aux[cont] = cad[i];
cont++;
}
i++;
}
cad = (char*)realloc(cad, sizeof(char) * cont);
i = 0;
while(aux[i] != '\0'){
cad[i] = aux[i];
i++;
}
}
Now I have a segmentation fault
You do not check the result of the realloc.
IMO it will be better to return the pointer to the new string instead of using double pointer. Double pointer may cause hard to track memory leaks, and function will not work with the const strings - for example string literals
You do not null character terminate the string.
In this example, I did not change your allocation algorithm but in real life more efficient will be first to count how much memory you need to allocate, allocate it and then process the string again:
char *delete(const char *cad, char c){
size_t nchars = 0;
char *aux = NULL;
char *temp;
while(*cad)
{
if(*cad != c)
{
temp = realloc(aux, sizeof(*temp) * nchars + 1);
if(temp)
{
aux = temp;
aux[nchars++] = *cad;
}
else
{
/* handle allocation error */
free(aux);
aux = NULL;
break;
}
}
cad++;
}
if(aux) aux[nchars] = 0;
return aux;
}
Some minor changes: use objects instead of types in sizeof and do not cast result of malloc. You can also add NULL pointer parameter check.
Every time you are reallocing inside the while loop, you are essentially giving the variable aux a new address each time.
I advise you to not do that and allocate the memory you want to allocate at the start of the function.
You will need to calculate how much memory you would need before allocating the memory. That is, count how much element you would delete.
If you want me to further elucidate or add a code fragment, please feel free to ask it in the comments.
Instead of many calls to realloc() I would just perform an in-place substitution of the characters; this substitution leaves unused allocated characters at the end of the string and is illustrated by the delete_no_realloc() function below.
If you want to get rid of these unused ending characters in the allocated string, then only one call to realloc() is needed as illustrated by the delete() function below.
Note that when a function uses realloc() on a parameter which is a pointer, it must obtain the address of this pointer to adjust it with the result of realloc().
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t // new length
delete_no_realloc(char *cad,
char c)
{
size_t w=0;
for(size_t r=0; cad[r]; ++r)
{
char ch=cad[r];
if(ch!=c)
{
cad[w++]=ch; // store and advance write index
}
}
cad[w]='\0'; // ensure string termination
return w;
}
void
delete(char **cad_ptr,
char c)
{
char *cad=*cad_ptr; // forget this embarrassing indirection
size_t new_length=delete_no_realloc(cad, c);
cad=realloc(cad, new_length+1);
if(cad==NULL)
{
abort();
}
*cad_ptr=cad; // don't forget to adjust the string
}
int
main(void)
{
const char *msg="this is a message";
char *cad=malloc(strlen(msg)+1);
if(cad==NULL)
{
abort();
}
strcpy(cad, msg);
printf("before: <%s>\n", cad);
delete(&cad, 's'); // pass the address of the string
printf("after: <%s>\n", cad);
free(cad);
return 0;
}
You can simplify your delete() function by simply using a read and write index within the original string, removing all c characters found, and then make a single call to realloc() to reallocate storage to exactly fit the remaining characters.
You can do something like:
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
A full example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
int main (int argc, char **argv) {
if (argc < 3) {
fputs ("usage: ./prog \"string with c\" c\n", stderr);
return 1;
}
size_t len = strlen (argv[1]);
char *s = malloc (len + 1);
if (!s) {
perror ("malloc-s");
return 1;
}
memcpy (s, argv[1], len + 1);
printf ("%s (%zu chars)\n", s, len);
delete (&s, *argv[2]);
printf ("%s (%zu chars)\n", s, strlen(s));
free (s);
}
Example Use/Output
$ ./bin/delete_c_realloc "nmyn ndogn nhasnn nnfleasnnn" n
nmyn ndogn nhasnn nnfleasnnn (28 chars)
my dog has fleas (16 chars)
Look things over and let me know if you have questions.
There are four main problems with your function implementation.
The first one is that the function accepts the pointer to the source string by value. That is the parameter cad is initialized by the value of the pointer used as an argument. As a result changing the variable cad does not influence on the original pointer.
The second one is that you are not checking whether a call of realloc was successful. As a result the function can invoke undefined behavior.
The third one is that it is inefficient to reallocate the string each time when a new character is appended.
And at last the fourth one is that the result dynamically allocated array does not contain a string because you forgot to append the terminating zero character '\0'.
If you want to change within the function a value of the original pointer you should either to return from the function the result pointer obtained in the function and assign it to the original pointer in the caller. Or you should pass the original pointer to the function by reference. In C passing by reference means passing an object (that can be a pointer) indirectly through a pointer to it.
Here is a demonstrative program that shows the function implementation when the original pointer is accepted by the function by reference.
The function also returns a pointer to the result string that can be checked in the caller whether the reallocation of dynamic memory within the function was successful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_char( char **s, char c )
{
char * result = *s;
if ( c != '\0' )
{
char *dsn = *s;
const char *src = *s;
do
{
if ( *src != c )
{
if ( dsn != src )
{
*dsn = *src;
}
++dsn;
}
} while ( *src++ );
char *tmp = realloc( *s, ( dsn - *s ) * sizeof( char ) );
if( tmp != NULL ) *s = tmp;
result = tmp;
}
return result;
}
int main(void)
{
char *s = malloc( 12 );
strcpy( s, "H#e#l#l#o!" );
puts( s );
if ( remove_char( &s, '#' ) ) puts( s );
free( s );
return 0;
}
The program output is
H#e#l#l#o!
Hello!
Another approach is to write a function that does not change the source string but creates dynamically a new string that contains the source string excluding the specified character. Such a function is more flexible because you can call it with string literals. If the source string also was dynamically allocated then the caller of the function after a successful call it can just free the source string.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_copy( const char *s, char c )
{
size_t src_len = strlen( s );
size_t dsn_len = src_len;
if ( c != '\0' )
{
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
--dsn_len;
}
}
char *result = malloc( ( dsn_len + 1 ) * sizeof( char ) );
if ( result != NULL )
{
const char *src_s = s;
char *dsn_s = result;
if ( dsn_len != src_len )
{
for ( const char *p = src_s;
( p = strchr( src_s, c ) ) != NULL;
src_s = p + 1 )
{
if ( p - src_s != 0 )
{
memcpy( dsn_s, src_s, p - src_s );
dsn_s += p - src_s;
}
}
}
strcpy( dsn_s, src_s );
}
return result;
}
int main(void)
{
char s[] = "H#e#l#l#o!";
puts( s );
char *p = remove_copy( s, '#' );
if ( p != NULL ) puts( p );
free( p );
return 0;
}
The program output is the same as shown for the preceding demonstrative program that is
H#e#l#l#o!
Hello!
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.
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.
I keep getting 4 "Passing argument 1 of strcpy makes pointer from integer without a cast" error message each time I am trying to write a string to a dynamically allocated array of strings. I know that it has to do with my strcpy call obviously, and that it's a type mismatch issue somewhere, but I need a little assistance on this please.
/* ---- LIBRARIES ---- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ---- PROTOTYPES ---- */
int readFx(char** arr);
/* int sortFx(char** arr, int arg2);
int printFx(char** arr, int arg2); */
int freeFx(char** arr, int cnt);
char* getToken(char arr1[], int loc);
void makeRoom(char*** t, int size);
/* ---- MAIN ---- */
int main(void)
{
char** pntrArr;
char* fileText;
int iniArrSize = 10;
int recCnt = 0;
/* array to store addresses of arrays forming the rows */
pntrArr = malloc(iniArrSize * sizeof(char*));
recCnt = readFx(pntrArr);
sortFx(pntrArr, recCnt);
/* printFx(pntrArr, recCnt); */
freeFx(pntrArr, recCnt);
return;
}
/* ---- FUNCTIONS ---- */
int readFx(char** arr)
{
/*
input: csv file of string arrays
output: count of records received
purpose: read file, store values in array and populate pointer array
*/
char buffer[350];
char temp[350];
char*** reallocTemp;
char* token;
int counter, index;
int subLoc = 3;
int enrLoc = 8;
int arrSize = 10;
/* Clear headers */
fgets(buffer, sizeof(buffer), stdin);
counter = 0;
/* While file stream is not null */
while (fgets(buffer, sizeof(buffer), stdin) != NULL)
{
/* Populate array within array if pntr arr has room */
if(counter <= arrSize)
{
/* buffer copy*/
strcpy(temp, buffer);
index = 0;
/* create array for token values */
arr[counter] = malloc(10 * sizeof(char));
/* Get first token */
token = getToken(temp, subLoc);
strcpy(arr[counter][index],token);
index++;
/* Get second token */
token = getToken(temp, enrLoc);
strcpy(arr[counter][index], token);
counter++;
}
else
{
/* Reallocate memory due to necessary expansion */
makeRoom(&arr, arrSize);
/* Realloc was successful */
if(temp != NULL)
{
arrSize = arrSize * 2;
/* Print Reallocation info */
printf("reallocating to %d", arrSize);
/* Populate values for current buffer now that you have realloc'd */
/* buffer copy*/
strcpy(temp, buffer);
index = 0;
/* create array for token values */
arr[counter] = malloc(10 * sizeof(char));
/* Get first token */
token = getToken(temp, enrLoc);
strcpy(arr[counter][index], token);
index++;
/* Get second token */
token = getToken(temp, subLoc);
strcpy(arr[counter][index], token);
counter++;
}
else
{
printf("unable to reallocate\n");
exit(1);
}
}
return counter;
}
char* getToken(char arr1[], int loc)
{
/*
input: string array & location of desired string
output: string of token at position
purpose: grab string (char*) of certain position in given array
*/
int loopCnt;
char* del = ",\n";
/* Grab first token */
char* token = strtok(buffer, del);
/* Loop through array to grab value at given location */
for(loopCnt = 1; loopCnt < loc; loopCnt++)
{
token = strtok(NULL, del);
}
return token;
}
int freeFx(char** arr, int cnt)
{
int i;
for(i = 0; i < cnt; i++)
{
free(arr[i]);
}
free( arr );
return 0;
}
void makeRoom(char*** t, int size)
{
*t = realloc(*t, size * 2 * sizeof(char*));
}
From code :
strcpy(arr[counter][index],token);
arr[counter][index] refers to the single character of the array, so please use arr[counter] instead of that (which is pointer to the dynamically allocated block),
so that you can copy the string to that dynamic array.
Adding extra details, if you use strcpy as below,
strcpy(arr[counter][index],token);
Still the copy is done but, not in the correct location,
Suppose arr[counter] holds the address of a char pointer which is 1000.
So strcpy(arr[counter][index],token); will copy the string in the address 1000 + index.
strcpy(arr[counter],token) will copy the string in the address 1000
Hope this is helpful.
How would I assign the value from strtok() to an array that's in a struct? Inside my struct I have char *extraRoomOne and in my main I have:
while (token!= NULL)
{
token = strtok(NULL, " ");
certainRoom.extraRoomOne[counter] = token;
}
Compiler is telling me to dereference it, but when I do I get a seg fault.
typedef struct room{
char *extraRoomOne;
}room;
In main, all I had was `room certainRoom;
Edit: changed char *extraRoomOne to char **extraRoomOne
Now I have:
token = strtok(NULL," ");
certainRoom.extraRoomOne = realloc(certainRoom.extraRoomOne,(counter + 1) * sizeof(char *));
certainRoom.extraRoomOne[counter] = malloc(strlen(token)+1);
strcpy(certainRoom.extraRoomOne[counter],token);`
Is this the correct way of realloc and malloc? I increment the counter below each time as well
You should not do that assignment because strtok() returns a pointer to the string you passed in the first call and it will change it in subsequent calls, and the '\0' terminator can be moved by strtok() so the pointer will point to a different string at the end, but instead you can copy the string first allocating space for it with malloc() and then with strcpy()
size_t length;
length = strlen(token);
certainRoom.extraRoomOne = malloc(1 + length);
if (certainRoom.extraRoomOne != NULL)
strcpy(certainRoom.extraRoomOne, token);
you should remember to include string.h.
And if what you really want is to capture more than just one token, which would explain the while loop, you could do it this way
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct room{
char **tokens;
size_t count;
} room;
room
tokenizeString(char *string)
{
char *token;
room instance;
instance.tokens = NULL;
instance.count = 0;
token = strtok(string, " ");
while (token != NULL)
{
void *pointer;
size_t length;
pointer = realloc(instance.tokens, (1 + instance.count) * sizeof(char *));
if (pointer == NULL)
{
size_t i;
for (i = 0 ; i < instance.count ; ++i)
free(instance.tokens[i]);
free(instance.tokens);
instance.tokens = NULL;
instance.count = 0;
return instance;
}
instance.tokens = pointer;
length = strlen(token);
instance.tokens[instance.count] = malloc(1 + length);
if (instance.tokens[instance.count] != NULL)
strcpy(instance.tokens[instance.count], token);
instance.count += 1;
token = strtok(NULL, " ");
}
return instance;
}
int
main(int argc, char **argv)
{
room certainRoom;
size_t i;
if (argc < 1) /* invalid number of arguments */
return -1;
certainRoom = tokenizeString(argv[1]);
for (i = 0 ; i < certainRoom.count ; ++i)
{
printf("%s\n", certainRoom.tokens[i]);
/* we are done working with this token, release it */
free(certainRoom.tokens[i]);
}
/* all tokens where released, now released the main container,
* note, that this only contained the pointers, the data was
* in the space pointed to by these pointers. */
free(certainRoom.tokens);
return 0;
}