cant find whos the owner of malloc - memory leak - c

I have a problem with a memory leak with temp_line.
This function read a whole text from a file and assign each word to a big linked list. I cant figure out who's the owner of temp_line whenever I exit this function, and whenever I am trying to replace temp_line with non-dynamic variable (like temp_line[1000]) every time I get a new line, its overwritten the data from the old line (and then I am getting a segmentation fault because of that).
So I really don't know how to solve it.
int fill(FILE *f, LinkedList *linkedlist) {
FILE *file = fopen((const char *)f, "r");
char line[MAX_LINE]; //MAX_LINE = 1000
while (fgets(line, MAX_LINE, file) != NULL) {
char *temp_line = malloc(MAX_LINE);
if (temp_line == NULL) {
fclose(file);
return false;
}
strcpy(temp_line, line);
read_line(linkedlist, temp_line);
}
fclose(file);
return true;
}
int read_line(LinkedList *linkedlist, char *line) {
char *word;
while (true) {
word = strtok(NULL, "\n ");
if (word == NULL) {
break;
}
add_node(linkedlist, word);
}
return true;
}
add_node adds the node to the linked list and returns the node.

There are multiple problems in the code:
FILE *file = fopen((const char *)f, "r"); with f defined as FILE *f is probably incorrect. If you are given a FILE *, just read from it.
strtok should be first called with the string, then with NULL until it returns NULL.
you insert nodes into the list from pointers into the middle of the block allocated by read_line. There is no way to determine the beginning of such a block, nor any way to determine how many pointers point into the block. Memory allocated and used this way cannot be freed. You should instead use strdup() to allocate individual copies of the words. These pointers can be later freed with free() in the del_node() function. Alternately, add_node() could make a copy of the string argument, which would be consistent with del_node() freeing this data.
regarding your question: Cannot find who is the owner of a malloc block... indeed it is the C programmers' responsibility to keep track of allocated memory. There is no way to test if a pointer is valid, not whether it points to an allocated block. You must design the program consistently so memory ownership can always be determined from context.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int read_line(LinkedList *linkedlist, char *line) {
char *word;
while ((word = strtok(line, "\n ")) != NULL) {
add_node(linkedlist, strdup(word));
line = NULL;
}
return true;
}
int fill(FILE *file, LinkedList *linkedlist) {
char line[MAX_LINE];
while (fgets(line, sizeof line, file) != NULL) {
read_line(linkedlist, line);
}
return true;
}

I am trying to replace temp_line with non-dynamic variable (like temp_line[1000])
temp_line[1000] is also called "automatic memory" because it is automatically deallocated at the end of its scope. What this means is temp_line would be deallocated on each iteration of the loop.
Perhaps the important thing to realize is strtok does not allocate any memory. word points at memory inside temp_line.
Let's say you did.
while (fgets (line, MAX_LINE, file) != NULL) {
char temp_line[MAX_LINE];
strcpy(temp_line, line);
read_line(linkedlist, temp_line);
}
On each loop temp_line will be allocated, passed into read_line, and then deallocated. The words you add to the linked list point at deallocated memory.
The question is who "owns" memory. Because it uses strtok, read_line assumes that it owns line and that line will not be deallocated. The safe thing to do is to change read_line so it copies each word from line.
int read_line(LinkedList *linkedlist, char *line) {
for(
char *word = strtok(line, "\n ");
word;
word = strtok(NULL, "\n ")
) {
// Duplicate the word so it no longer refers to line.
add_node(linkedlist, strdup(word));
}
return true;
}

Related

Save first word a file in c

I have a problem with this part of my code, I'm trying to read the lines of a file and cut only the first word of each line and then save it in an array.
Example:
two roads diverged in a yellow wood
and sorry i could not travel both
and be one traveler long i stood
and looked down one as far as i could
to where it bent in the undergrowth
and as a result I expect a vector like this: "two, and, and, and, to"
but I get this: "to, to, to, to, to".
My code
dictionary *load_word(int autor, dictionary *D_first)
{
FILE *date;
char line[LONG_MAX_LINE];
char exeption[4] = " \n\t";
char *word;
int j=0;
if (autor == 1)
{
if ((date = fopen("test.txt", "r")) == NULL)
{
perror("robert_frost.txt");
}
while (fgets(line, LONG_MAX_LINE, date ) != NULL)
{
word = strtok(line, exeption); /*first word*/
add_dictionary_first(D_first, j, word);
j++;
}
fclose(date);
}
return D_first;
}
void add_dictionary_first(dictionary *D, int cont, const char *value)
{
expand_dictionary(&D, 1);
D->Distribution[D->size-1]->cont = cont;
D->Distribution[D->size-1]->value = value;
}
The problem lies within this line (as Vlad from Moscow posted in the comments):
D->Distribution[D->size-1]->value = value;
This is just pointer assignment. That's not wrong per se, but depending on the
context, it is not what you want.
while (fgets(line, LONG_MAX_LINE, date ) != NULL)
{
word = strtok(line, exeption); /*first word*/
add_dictionary_first(D_first, j, word);
...
}
Here you call add_dictionary_first always with the same variable line. It is
an array but arrays decay into pointers when passing them as arguments to
functions. That means that all your D->Distribution[D->size-1]->value point to
the same location. The last line in your input file begins with to and that's why you get only
to.
You need to copy the string with strcpy.
man strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
The strcpy() function copies the string pointed to by src, including the terminating
null byte ('\0'), to the buffer pointed to by
dest. The strings may not overlap, and the destination string dest must be
large enough to receive the copy.
Because you haven't posted the structure I can only guess that value is
declared as char* (if it were char[] the compiler would have complained).
Option 1
D->Distribution[D->size-1]->value = malloc(strlen(value) + 1); // note the +1 here
if(D->Distribution[D->size-1]->value == NULL)
{
// error handling
}
strcpy(D->Distribution[D->size-1]->value, value);
Option 2
If strdup is available in your system
D->Distribution[D->size-1]->value = strdup(value);
if(D->Distribution[D->size-1]->value == NULL)
{
// error handling
}
In either case you would have to free the memory later.

C - list of char*'s - Memory Allocation

I am confused about how to allocate memory correctly. I am trying to make a list of char*'s from a text file. Every time I make a char* do I have to allocate memory for it? When and where are the exceptions?
#define BUFF 1000
int main(int argc, char** argv)
{
FILE* file;
file = fopen(argv[1], "r");
char* word = calloc(BUFF, sizeof(char));
char* sentence = calloc(BUFF, sizeof(char));
char** list = calloc(BUFF, sizeof(char*));
int i = 0;
while((fgets(sentence, BUFF, file)) != NULL)
{
word = strtok(sentence, " ,/.");
while(word != NULL)
{
printf("%s\n", word);
strcpy(list[i], word);
i++;
word = strtok(NULL, " ,/.");
}
}
int k;
for(k = 0; k < i; k++)
{
puts("segging here");
printf("%s\n", list[i]);
}
The rule is: you have to allocate any memory that you use.
Your problem comes in:
strcpy(list[i], word);
list[i] is currently not pointing to any allocated storage (it's probably a null pointer). You have to make it point somewhere before you can copy characters into it.
One way would be:
list[i] = strdup(word);
strdup is not an ISO C standard function, but it is equivalent to doing malloc then strcpy. You will need to free afterwards.
Also, the i++ line needs to stop when i == BUFF, and it'd be useful to add \n to the list of strtok separators .
In addition to Matt McNabb's answer, there's also a more subtle problem with your usage of strtok. That function doesn't require an output buffer; it just returns a pointer to somewhere inside the input buffer.
When you call char* word = calloc(BUFF, sizeof(char));, you allocate memory and assign word to point to the allocated memory. Then, when you call word = strtok(sentence, " ,/.");, you overwrite the value of word. That means that no pointer in your control points to the memory you've allocated. That memory is no longer useful to your code, and you can't deallocate it; it has been leaked.
You can fix this issue by writing char* word = strtok(sentence, " ,/."); Then, since you didn't allocate the memory that word points to, remember not to free it either.
your list is a char* list, with the size of BUFF, but the list[i] is what?
you do not allocate memory to it.
you need to allocate the memory for list[i] in a loop

Array of structs replacing values over itself

Ok so I have the below code and I am just pulling various things from a file and inputing them in an array of structs, it "seemingly" works initially, BUT when I go to printing it after it is done with the file it seemed to have replaced all of the courses and names with the very last vale, oddly this doesnt happen with the integers (grades), the grades do get inputed properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
char *name;
char *course;
int grade;
};
void courseSort(struct student d[20], int size);
int main(void)
{
FILE* fp;
char* filename = "grades.csv";
char buffer[100];
char* name, *class;
char* del=",";
int grade, i, counter=0;
struct student d[20];
if((fp=fopen(filename, "r"))==NULL)
{
printf("unable to open %s\n", filename);
exit(1);
}
while(fgets(buffer, sizeof(buffer), fp) !=NULL)
{
name = strtok(buffer,del);
class=strtok(NULL,del);
grade = atoi(strtok(NULL,del));
d[counter].name=name;
d[counter].course=class;
d[counter].grade=grade;
printf("%s %s %d\n",d[counter].name,d[counter].course,d[counter].grade);
counter++;
}
printf("\n\n\n");
for(i=0;i<counter;i++)
printf("%s %s %d\n",d[i].name,d[i].course,d[i].grade);
courseSort(d,counter);
fclose(fp);
}
I am not sure what I am doing wrong :( it all seems straightforward but not sure why it just replaces everything with the latest one.
The strtok returns a pointer to the buffer and does not allocate memory. Since you do not copy the strings, you end up with lots of pointers pointing to the same buffer that is overwritten at each iteration of the loop.
To fix this, you need to change your loop to copy the strings using strdup:
while(fgets(buffer, sizeof(buffer), fp) != NULL)
{
d[counter].name = strdup(strtok(buffer, del));
d[counter].course = strdup(strtok(NULL, del));
d[counter].grade = atoi(strtok(NULL, del));
counter++;
}
Don't forget to return the allocated memory with free once you no longer need the strings:
for (i = 0; i < counter; i++) {
free(d[i].name);
free(d[i].course);
d[i].name = NULL;
d[i].course = NULL;
}
Note that strdup is part of POSIX1.2001 standard, not part of C89. If it is not available, you'll have to re-implement it yourself (quite easy):
char *my_strdup(const char *str) {
char *copy;
size_t len = strlen(str) + 1;
if (len == 0) return NULL;
copy = (char *)malloc(len);
if (copy == NULL) return NULL;
memcpy(copy, str, len);
return copy;
}
This is a simple misunderstanding of pointers and char arrays (strings). Here are a couple pages that explains them pretty well:
http://www.cplusplus.com/doc/tutorial/pointers/
http://www.cplusplus.com/doc/tutorial/ntcs/
In your case, you are setting your struct pointer values equal to the returned pointer from strtok. A lot of those string functions work by putting the result at a certain memory address and returning the pointer to it. The pointer returned is always the same, so all your struct values are going to point to the last result of the strtok call.
This is why you need strdup (String duplicate). Basically it takes the value at the address given and copies the contents into a new place in memory and returns the value.
The error is here.
d[counter].name=name;
replace with:
d[counter].name = strdup(name); /*don't forget to free this memory.*/
the issue for the courses is the same.

how to put char * into array so that I can use it in qsort, and then move on to the next line

I have lineget function that returns char *(it detects '\n') and NULL on EOF.
In main() I'm trying to recognize particular words from that line.
I used strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
Now I have no idea how to put every of tokenized words into an array (I created char ** helper for that purpose), so that it can be used in qsort like qsort(helper, strtoks, sizeof(char*), compare_string);.
Ad. 2 Even if it would work - I don't know how to clear that line, and proceed to sorting next one. How to do that?
I even crashed valgrind (with the code presented above) -> "valgrind: the 'impossible' happened:
Killed by fatal signal"
Where is the mistake ?
The most obvious problem (there may be others) is that you're reallocating helper to the value of strtoks at the beginning of the line, but then incrementing strtoks and adding to the array at higher values of strtoks. For instance, on the first line, strtoks is 0, so temp = realloc(helper, (strtoks)*sizeof(char *)); leaves helper as NULL, but then you try to add every word on that line to the helper array.
I'd suggest an entirely different approach which is conceptually simpler:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
I haven't tested this so let me know if it's not correct or there's anything you don't understand. I've left out the opening and closing of files and the freeing at the end (remember you have to free every element of helper before you free helper itself).
As you can see in strtok's prototype:
char * strtok ( char * str, const char * delimiters );
...str is not const. What strtok actually does is replace found delimiters by null bytes (\0) into your str and return a pointer to the beginning of the token.
Per example:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
Now look at the results:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
As you can see, toks[1] and &in[4] point to the same location: the original str has been modified, and in reality all tokens in toks point to somewhere in str.
In your case your problem is that you free line:
free(line);
...invalidating all your pointers in helper. If you (or qsort) try to access helper[0] after freeing line, you end up accessing freed memory.
You should copy the tokens instead, e.g.:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
Obviously, you will need to free each element of helper afterwards (in addition to helper itself).
You should be getting a 'Bad alloc' error because:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
This is because the value of strtoks is zero, so you are asking realloc() to free the memory pointed at by helper (which was itself a null pointer). One outside chance is that your library crashes on realloc(0, 0), which it shouldn't but it is a curious edge case that might have been overlooked. The other possibility is that realloc(0, 0) returns a non-null pointer to 0 bytes of data which you are not allowed to dereference. When your code dereferences it, it crashes. Both returning NULL and returning non-NULL are allowed by the C standard; don't write code that crashes regardless of which behaviour realloc() shows. (If your implementation of realloc() does not return a non-NULL pointer for realloc(0, 0), then I'm suspicious that you aren't showing us exactly the code that managed to crash valgrind (which is a fair achievement — congratulations) because you aren't seeing the program terminate under control as it should if realloc(0, 0) returns NULL.)
You should be able to avoid that problem if you use:
temp = realloc(helper, (strtoks+1) * sizeof(char *));
Don't forget to increment strtoks itself at some point.

fscanf is somehow changing a node (in c)

I am new to c and have been stuck on this bug for hours. My code reads each word from a txt file and then stores the word in a node in a trie.
I have narrowed the problem down to the area marked off by asterisks:
at that point I have successfully added the first word to the trie and check that the correct node's value matched the word. Then, I use fscanf to save the next word in the file as the char 'add'. Then, printing out the exact same thing as before, the node's word should have remained the same. However, it has somehow been changed to the new word that was just read from the file.
How is this even possible??
int main(int argc, char** argv) {
trie_t* trie = malloc(sizeof(trie_t));
trie_init(trie);
int ret;
char* add = malloc(128);
FILE* file = fopen(argv[5], "r");
if (file == NULL) {
/* Failed to open the file for reading */
return 0;
}
while (1) {*********************
if (trie->head->children != NULL) {
printf("%s\n", trie->head->children->word);
}
ret = fscanf(file, "%s", add);
//printf("word = %s\n",toAdd);
if (trie->head->children != NULL) {
printf("%s\n", trie->head->children->word);
}****************************
if (ret == EOF) {
/* End of file */
ret = 1;
break;
} else if (ret <= 0) {
printf("fails");
/* Failed to read a word from the file */
break;
} else {
printf("gets here\n");
/* Succesfully read a word */
int x = trie_add(trie, add);
printf("%d\n",x);
}
}
You are only assigning memory once for add at:
char* add = malloc(128);
You need to assign memory for each word, that is, you need to move the malloc to your read cycle.
What the code is doing as you posted it is: allocate 128 bytes once, and then overwrite that memory space once and again every time you scanf().
Also, at char* add = malloc(128); you should assign it as char* add = malloc(128 * sizeof(char)); just to be clear and portable :)
I assume you are storing the pointer add in trie_t in trie_add function. In this case, since you are reusing the same memory location add for reading the next string, the content of the pointer pointed by add changes. Since you are just storing this pointer in trie the content of that node also changes because of this. To solve this, you need to allocate memory again using malloc just before the new string is read from file using fscanf.

Resources