I have a function that takes a string and split it into tokens, because I want to return these tokens I allocate a variable using malloc.
char** analyze(char* buffer)
{
int i= 0;
char* token[512];
char** final = (char**)malloc(strlen(buffer)+1);
if ( final == NULL ) { perror("Failed to malloc"); exit(10); }
token[i] = strtok(buffer, " ");
while( token[i] != NULL )
{
final[i] = malloc(strlen(token[i])+1);
if( final[i] == NULL ) { perror("Failed to malloc"); exit(11); }
final[i] = token[i];
i++;
token[i] = strtok(NULL, " ");
}
final[i] = malloc(sizeof(char));
if( final[i] == NULL ) { perror("Failed to malloc"); exit(12); }
final[i] = NULL;
return final;
}
And I try to free this table with another function:
void free_table(char** job)
{
int i = 0;
while( job[i] != NULL )
{
free(job[i]);
i++;
}
free(job[i]); //free the last
free(job);
}
In main I use:
char** job = analyze(buffer); // buffer contains the string
and
free_table(job);
when I try to free the table I get this error:
*** Error in `./a.out': free(): invalid pointer: 0x00007ffd003f62b0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fdb2e5497e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x7fe0a)[0x7fdb2e551e0a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fdb2e55598c]
./a.out[0x4012d6]
and the error goes on...
What am I doing wrong?
To begin with:
char** final = (char**)malloc(strlen(buffer)+1);
This allocates strlen(buffer) + 1 bytes, not that amount of "elements". And since sizeof(char*) is most likely very much larger than a single byte, you might be allocating to little memory here.
Since you don't know how many tokens there might be you should not allocate a fixed amount, but instead use realloc to reallocate as needed.
Then the second problem:
final[i] = malloc(strlen(token[i])+1);
...
final[i] = token[i];
In the first statement you allocate memory enough for the string pointed to by token[i], and assign the pointer to that memory to final[i]. But then you immediately reassign final[i] to point somewhere else, some memory that you haven't gotten from malloc. You should copy the string instead of reassigning the pointer:
strcpy(final[i], token[i]);
On an unrelated note, there's no need for token to be an array of pointer. It can be just a pointer:
char *token = strtok(...);
Example of a possible implementation:
char **analyze(char *buffer)
{
size_t current_token_index = 0;
char **tokens = NULL;
// Get the first "token"
char *current_token = strtok(buffer, " ");
while (current_token != NULL)
{
// (Re)allocate memory for the tokens array
char **temp = realloc(tokens, sizeof *temp * (current_token_index + 1));
if (temp == NULL)
{
// TODO: Better error handling
// (like freeing the tokens already allocated)
return NULL;
}
tokens = temp;
// Allocate memory for the "token" and copy it
tokens[current_token_index++] = strdup(current_token);
// Get the next "token"
current_token = strtok(NULL, " ");
}
// Final reallocation to make sure there is a terminating null pointer
char **temp = realloc(tokens, sizeof *temp * (current_token_index + 1));
if (temp == NULL)
{
// TODO: Better error handling
// (like freeing the tokens already allocated)
return NULL;
}
tokens = temp;
// Terminate the array
tokens[current_token_index] = NULL;
return tokens;
}
Note that strdup isn't a standard C function, but it is prevalent enough to assume it will exist. In the unlikely case where it doesn't exist, it's easy to implement yourself.
Related
I'm working with c90 on linux.
I have a strange bug when I want to end a string,
let idx be the index, so when I get to the last index I want the list[idx] to be NULL.
example:
list[0] actually "hello"
list[1] actually "world\n"
list[2] sometimes is "" or NULL
so when I put NULL to the the end of the list its deletes one of the other words..
for: list[2] = NULL;
unexpectedly list[0] turns NULL but list[1] still "world\n" and list[2] of course NULL.
I wrote this function:
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = (char**)malloc((sizeof(char*)));
get_input(buffer, list, &list_len);
/*
some other code
*/
}
free_list(list, list_len); /*free the array of strings words*/
}
and wrote also the get_input because I work with c90
void get_input(char* line, char** list, int *idx)
{
char * token;
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token && token[0] == '\t')
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list[*idx] = (char *)malloc(strlen(token)+1);
strncpy(list[*idx], token, strlen(token));
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
}
if (*idx == 0)
list = NULL;
list[*idx - 1][strcspn(list[*idx - 1], "\n")] = 0; /* remove the "\n" */
list[*idx] = NULL; /* to know when the list ends */
}
the free function:
void free_list(char** list, int list_len)
{
int i;
for(i= list_len - 1; i >= 0; i--)
{
list[i] = NULL;
free(list[i]);
}
}
You have multiple issues.
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = (char**)malloc((sizeof(char*)));
get_input(buffer, list, &list_len);
/*
some other code
*/
}
free_list(list, list_len); /*free the array of strings words*/
}
You only allocate memory for 1 pointer.
You only free the pointers in the last list.
You never free the memory for list ifself.
You should not cast the return value of malloc and friends.
This should be changed like this:
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = malloc((sizeof(char*)));
get_input(buffer, &list, &list_len);
/*
some other code
*/
free_list(list); /*free the array of strings words*/
free(list);
}
}
The freeing function is also broken:
void free_list(char** list, int list_len)
{
int i;
for( i= list_len - 1; i >= 0; i--)
{
list[i] = NULL;
free(list[i]);
}
}
You set the pointer within list to NULL before you free it. This causes a memory leak as the memory is not really freed.
You don't really need the length as you have added a sentinel. But that is not an error.
There is also no need to free the pointers backwards.
After cleanup the function could look like this:
void free_list(char** list)
{
while (list[i])
{
free(list[i]);
i++;
}
}
Now the biggest part:
void get_input(char* line, char** list, int *idx)
{
char * token;
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token && token[0] == '\t')
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list[*idx] = (char *)malloc(strlen(token)+1);
strncpy(list[*idx], token, strlen(token));
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
}
if (*idx == 0)
list = NULL;
list[*idx - 1][strcspn(list[*idx - 1], "\n")] = 0; /* remove the "\n" */
list[*idx] = NULL; /* to know when the list ends */
}
You do not care about memory for the pointers in your list. That means you store the pointers in memory that you are not allowed to touch. By doing this you invoke undefined behaviour.
You must realloc the memory and for that you must be able to modify the passed pointer.
You should not cast the return values of malloc and friends.
You access illegal index values if *idx==0
You call strncpy with the length of the string without space for the 0 byte. That will cause the copy to be not nul terminated. Also there is no need to use strncpy over strcpy as you have reserved enough memory.
void get_input(char* line, char*** list, int *idx)
{
char *token;
char **list_local = *list; // Make things easier by avoiding one * within the function.
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token[0] == '\t') // No need to check for `token` again
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list_local[*idx] = malloc(strlen(token)+1);
strcpy(list_local[*idx], token);
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
/* Increase array size to hold 1 more entry. */
/* That new element already includes memory for the sentinel NULL */
{
char ** temp = realloc(list_local, sizeof(char*) * (*idx));
if (temp != NULL)
list_local = temp;
// TODO: error handling ...
}
}
if (*idx != 0)
{
list_local[*idx - 1][strcspn(list_local[*idx - 1], "\n")] = 0; /* remove the "\n" */
}
list_local[*idx] = NULL; /* to know when the list ends */
*list = list_local;
}
typedef struct{
char** strings_cmd;
int size_cmd;
}parseInfo;
....
parseInfo* parse(char* cmd){
char* temp = strdup(cmd);
char* temp_split = strtok(temp," ");
int i = 0;
char** strings = (char**)malloc(sizeof(char*));
if(strings == NULL){
printf("no memory allocated strings parse()\n");
exit(1);
}
while(temp_split != NULL){
strings[i++] = strdup(temp_split);
strings = realloc(strings,i * sizeof(char*));
if(strings == NULL){
printf("no memory allocated strings (while) parse()\n");
exit(1);
}
temp_split = strtok(NULL," ");
}
strings[i] = NULL;
parseInfo* info = (parseInfo*)malloc(sizeof(parseInfo));
if(info == NULL){
printf("no memory allocated info parse()\n");
exit(1);
}
info->strings_cmd = strings;
info->size_cmd = i;
return info;
}
hello guys i get the error:
realloc(): invalid next size.
and what i try to do is to input a string and split it down into words
for example i input = "Hello World".
and to split it = "Hello" , "World"
but when i pass 4 words i got this error...
For starters the function has a memory leak because in the beginning of the function there is allocated memory
parseInfo* parse(char* cmd){
char* temp = strdup(cmd);
//...
that was not freed.
In this while loop
while(temp_split != NULL){
strings[i++] = strdup(temp_split);
strings = realloc(strings,i * sizeof(char*));
if(strings == NULL){
printf("no memory allocated strings (while) parse()\n");
exit(1);
}
temp_split = strtok(NULL," ");
You need to wirte
strings = realloc(strings, ( i + 1 ) * sizeof(char*));
to reserve one element for the terminating null pointer used in this statement
strings[i] = NULL;
And you will need to free the allocated dynamically memory in the beginning of the function like
free( temp );
}
you are allocating an array of pointers with one less element that it is required.
This line is bad:
strings = realloc(strings,i * sizeof(char*));
This line is resizing the array to i elements.
Then, in the next iteration, some value is stored to the i-th element of the array (pointed at by) strings. The array has only i elements (0 to i-1), so this is out-of-range access.
Allocate enough elements to fix:
strings = realloc(strings,(i + 1) * sizeof(char*));
Also note that casting results of malloc() family is considered as a bad practice.
I am just starting to learn about malloc'd and realloc'd arrays. Can anyone help explain to me how to properly free my following array? I have tried looking at other posts, but I have a hard time understanding memory allocation in C.
char ** result = NULL;
int numSpaces = 0;
char * p = strtok(command, " ");
/* split string and append tokens to 'result' */
while (p)
{
result = realloc (result, sizeof (char*) * ++numSpaces);
if (result == NULL)
exit (-1); /* memory allocation failed */
result[numSpaces-1] = p;
p = strtok(NULL, " ");
}
Freeing realloc-ed memory is not different from freeing malloc-ed memory, in that all you need is to call free on the result at the end, when you no longer need the memory.
Your code, however, has a pattern that may lead to a memory leak: you are assigning realloc-ed block back to result without checking it for NULL. If realloc fails, the previous value of the result becomes unrecoverable, because it has been overwritten by NULL.
Here is how you could fix this issue:
char **temp = realloc(result, sizeof (char*) * ++numSpaces);
if (temp == NULL) {
free(result); // Free the old memory block
exit (-1); /* memory allocation failed */
}
result = temp;
At some point after you are done using result, you need to call free(result);.
This might look like:
char ** result = NULL;
int numSpaces = 0;
char * p = strtok(command, " ");
while (p) {
result = realloc (result, sizeof (char*) * ++numSpaces);
result[numSpaces-1] = p;
p = strtok(NULL, " ");
}
for (i=0; i<numSpaces; ++i)
printf("word %d: %s\n", i, result[i]);
free(result);
I need to save the contents of tmp to tmp2. However tmp is always NULL outside of the while loop.
if(1){
char* tmp;
char* tmp2;
// split the string on the space character
tmp = strtok(actual_args[0], " ");
while(tmp != NULL){
strcpy(tmp2, tmp);
tmp = strtok(NULL, " ");
}
// always NULL
printf("%s", tmp);
// produces seg. fault
printf("%s", tmp2);
}
The problem with your code is that it is not using strcpy correctly: the function copies the content of the string, it does not create a copy of the string's memory.
It is your task to allocate the memory for the destination string. You could do it in the automatic memory (i.e. on the stack), in the static memory, or in the dynamic memory (i.e. the heap).
If you want to allocate dynamic memory for your string, you can do it like this:
char tmp2 = NULL; // Don't forget to initialize tmp2
...
while(tmp != NULL){
free(tmp2); // Free the old content of tmp2
tmp2 = malloc(strlen(tmp)+1); // Add one byte for null terminator
strcpy(tmp2, tmp); // Now the copy has space to which the data is copied
tmp = strtok(NULL, " ");
}
... // Use tmp2 ...
free(tmp2); // You need to free dynamically allocated memory
You could use the non-standard strdup function for this as well, but that is not recommended.
If your goal is finding the last token:
// assuming actual_args[0] is a char *
char *lastToken = actual_args[0];
for (int i = 0; 0 != actual_args[0][i]; i++) {
if (' ' == actual_args[0][i]) lastToken = &actual_args[0][i+1];
}
printf("%s", actual_args[0]);
printf("%s", lastToken);
If you want an array of all the tokens, you could do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_TOKS 10
int main() {
char *p, *toks[MAX_TOKS];
char str[] = "a string to tokenize";
int i, n = 0;
p = strtok(str, " ");
while (p) {
if (n >= MAX_TOKS) {
fprintf(stderr, "MAX_TOKS overflow\n");
exit(EXIT_FAILURE);
}
toks[n++] = p;
p = strtok(NULL, " ");
}
for (i = 0; i < n; ++i)
printf("[%s]\n", toks[i]);
return 0;
}
What's the problem of doing this:
void *educator_func(void *param) {
char *lineE = (char *) malloc (1024);
size_t lenE = 1024;
ssize_t readE;
FILE * fpE;
fpE = fopen(file, "r");
if (fpE == NULL) {
printf("ERROR: couldnt open file\n");
exit(0);
}
while ((readE = getline(&lineE, &lenE, fpE)) != -1) {
char *pch2E = (char *) malloc (50);
pch2E = strtok(lineE, " ");
free(pch2E);
}
free(lineE);
fclose(fpE);
return NULL;
}
If i remove the line 'pch2E = strtok(lineE, " ");' it works fine...
why cant i do a strtok() there ? I tried with strtok_r() also but no luck, it gives me invalid free (Address 0x422af10 is 0 bytes inside a block of size 1,024 free'd)
Your code is not doing what you think it is doing... the call to pch2E = strtok(lineE, " "); is replacing the value of pch2E with the return value of strtok which is either lineE or a newly allocated replacement for lineE
You can fix it as follows...
int firstPass = 1;
while ((readE = getline(&lineE, &lenE, fpE)) != -1)
{
char* pch2E = strtok( firstPass ? lineE : NULL, " ");
firstPass = 0;
}
free(lineE);
I should add, the more I look at your code, the more fundamentally flawed it looks to me. You need an inner loop in your code that deals with tokens while the outer loop is loading lines...
while ((readE = getline(&lineE, &lenE, fpE)) != -1)
{
char* pch2E;
int firstPass = 1;
while( (pch2E = strtok( firstPass ? lineE : NULL, " ")) != NULL )
{
firstPass = 0;
// do something with the pch2E return value
}
}
free(lineE);
strtok returns a pointer to the token, that is included in the string you have passed, so you can't free it, because it doesn't (always) point to something you've allocated with malloc.
That kind of assignment can't even work in C, if you wanted a function that would copy the token into a buffer, it would be something like this:
tokenize(char* string, char* delimiter, char* token);
And you would need to pass a valid pointer to token, for the function to copy the data in. In C to copy the data in the pointer, the function needs access to that pointer so it would be impossible for a function to do it on a return value.
An alternative strategy for that (but worst) would be a function that allocates memory internally and returns a pointer to a memory area that needs to be freed by the caller.
For your problem, strtok needs to be called several times to return all the tokens, until it returns null, so it should be:
while ((readE = getline(&lineE, &lenE, fpE)) != -1) {
char *pch2E;
pch2E = strtok(lineE, " "); //1st token
while ((pch2E = strtok(NULL, " ")) != NULL) {
//Do something with the token
}
}