I use malloc in a function. End of the function I call free function to avoid memory leak. But if I call free I get segmentation fault when I don't program works normally. Can anybody know why? I marked the places with comments(If I put free(line) here I get seg fault. If I don't it occurs memory leak). Here is my code:
void checkOccurrences(FILE *inp, char *wanted, int lineCount, int limit)
{
char option;
char *line = malloc(INT_MAX * sizeof(char));
int i, dif, lineNumber = 0; /*i for count, dif means difference*/
char *temp, *checkpoint;
while(fgets(line, INT_MAX, inp) != NULL) /*gets line by line*/
{
dif = strlen(line) - strlen(wanted); /*for limit for loop*/
++lineNumber; /*increase line number*/
for(i = 0; i < dif; ++i) /*difference times loop*/
{
temp = strstr(line, wanted); /*first occurrence address on line*/
if(temp != NULL) /*if there is occurrence*/
{ /*temp>checkpoint condition means if there is new occurrences*/
if(temp > checkpoint)
{
++lineCount;
if((lineCount % limit) == 0)
{
printLine(lineNumber);
checkpoint = temp;
printf("more(m) or quit(q) ");
scanf(" %c", &option); /*get option*/
if(option == 'q')
{
/*If I put free(line) here I get seg fault*/
/*If I don't it occurs memory leak*/
exit(0); /*end program*/
}
}
else
{
printLine(lineNumber);
checkpoint = temp;
}
}
}
++line; /*next address on line*/
}
}
/*If I put free(line) here I get seg fault*/
/*If I don't it occurs memory leak*/
}
/*GIT121044025*/
Looks like you're changing line inside the loop, so you're not free'ing the same pointer you got from malloc()
You should make a copy of the pointer for modifying in the loop and leave the original alone.
You might also want to consider doing something less horrid than allocating a potentially enormous amount of memory up front, just in case, and then using it without error checks.
(you don't check the memory was ever allocated, and your fgets always assumes you have INT_MAX bytes available even after you increment the pointer into the buffer).
You lose your base address here:
++line; /*next address on line*/
Store line in a temp variable like this:
char *line = malloc(INT_MAX * sizeof(char));
char *_temp = line ;
And at the end of function:
free(_temp);
Related
Hi I read that I should call free() as soon as I could do that to free the memory but when I call free in this way my code stops working correctly. what's the problem?
I want to call free() in every iteration and when an error occurs.
int read_words(char *words[], int size, int max_str_len) {
int i, j;
char *ExtendedWord = NULL;
for (i = 0; i < size && size != -1; ++i) {
char tmp[1], ch, *word = tmp;
for (j = 0; j < max_str_len; ++j) {
if (scanf("%c", &ch) == EOF || ch == 'R') {
size = -1;
break;
}
if (ch == ' ')
break;
word[j] = ch;
ExtendedWord = malloc((i + 2) * sizeof(char));
if (ExtendedWord == NULL)
return -1;
strcpy(ExtendedWord, word);
word = ExtendedWord;
free(ExtendedWord);
}
word[j] = '\0';
words[i] = word;
}
return i;
}
strcpy(ExtendedWord,word);
strcpy() expects as 2nd parameter the address of the 1st character of a "C"-string, which in fact is a char-array with at least one element being equal to '\0'.
The memory word points to does not meet such requirements.
Due to this the infamous undefined behaviour is invoked, probably messing up the program's memory management, which in turn causes free() to fail.
There are multiple problems in your code:
you free the newly allocated block instead of the previous one.
you so not null terminate the string before passing it to strcpy
word should be initialized as NULL or to a block of allocated memory, not to point to a local array which you cannot pass to free().
you should reallocate the array before copying the new character at its end.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
int i, j;
for (i = 0; i < size; i++) {
char *word = malloc(1);
if (word == NULL)
return -1;
for (j = 0; j < max_str_len; ++j) {
int ch;
char *ExtendedWord;
if ((ch = getchar()) == EOF || ch == 'R') {
size = -1;
break;
}
if (ch == ' ' || c == '\n')
break;
/* reallocate array for one more character and a null terminator */
ExtendedWord = malloc(i + 2);
if (ExtendedWord == NULL)
return -1;
memcpy(ExtendedWord, word, i);
free(word);
word = ExtendedWord;
word[j] = ch;
}
if (size == -1) {
free(word);
break;
}
word[j] = '\0';
words[i] = word;
}
return i;
}
I Read that I should call free() as soon as I could do that to free the memory
That description is a little ambiguous. It is reasonable only if you interpret "as soon as I could do that" to mean the same as "as soon as I no longer need the allocated memory".
but when I call free in this way my code stops working correctly. what's the problem?
The problem with respect to free is that you free the memory before you are done with it. Subsequently attempting to access that memory produces undefined behavior.
There are other problems with the code, too, discussed in other answers, but this is how the free fits into the picture.
I want to call free in every iteration and when an error occurs.
Inasmuch as it appears that your function intends to provide pointers to the allocated memory to its caller via the words array, you must not free that memory anywhere within the scope of the function, because the caller (it must be presumed) intends to use it. Therefore the caller must assume the responsibility for freeing it. The function's documentation should clearly describe that responsibility.
Perhaps the confusion arises here:
word=ExtendedWord;
It is essential to understand that the assignment copies the pointer, not the space to which it points. Afterward, word points to the same (dynamically allocated) space that ExtendedWord does, so that freeing ExtendedWord invalidates both copies of the pointer.
I am trying to figure out how to modify my code to actually allow for me to create a array of structs in my readFile and then return the array to the main.
This is my data struct
struct data{
char *model;
float engineSize;
int cost;
char *color;
};
This is my current setup of my readFile function and then the call that I use currently for this function.
struct data * readFile(){
FILE *fp;
int c;
int count = 0;
char *line = NULL;
size_t len = 0;
fp = fopen("hw3.data", "r");
while ((c = fgetc(fp)) != EOF){
count++;
}
if (feof(fp)){
rewind(fp);
struct data *vehicles = malloc((sizeof(struct data))* count);
count = 0;
char *token = NULL;
while (getline(&line, &len, fp)!= -1){
printf("%s", line);
token = strtok(line, " ");
vehicles[count].model = token;
token = strtok(NULL, " ");
vehicles[count].engineSize = atof(token);
token = strtok(NULL, " ");
vehicles[count].cost = atoi(token);
token = strtok(NULL, " ");
vehicles[count].color = token;
}
}
}
This is the main where I have my menu and where I will do my call for my readFile function.
int main(){
int check = 1;
int input;
while (check == 1){
printf("Enter a value corresponding to a option on the menu below\n\n");
printf("1. Sort data by the float value & print high to low\n");
printf("2. Sort data by the float value & print low to high\n");
printf("3. Sort data by the int value & print high to low\n");
printf("4. Sort data by the int value & print low to high\n");
printf("5. Exit\n\n");
printf("Enter a value corresponding to the above menu\n");
scanf("%d", &input);
//readFile()
if(input == 1 || input == 2 || input == 3 || input == 4 || input == 5){
if (input == 5){
exit(0);
}if (input == 1){
//sort float high to low
}if (input == 2){
//sort float low to high
}if (input == 3){
//sort int value high to low
}if (input == 4){
//sort int value low to high
}
}else{
printf("Enter a correct value for the menus above\n\n" );
}
readFile();
}
}
Thanks
It's almost correct, the idea is OK but there are a few issues:
while ((c = fgetc(fp)) != EOF){
count++;
}
That counts the number of bytes, I think based on the later code you want the
number of lines.
while ((c = fgetc(fp)) != EOF){
if(c == '\n')
count++;
}
would give you the number of lines.
Down there
token = strtok(line, " ");
vehicles[count].model = token;
...
token = strtok(NULL, " ");
vehicles[count].color = token;
is valid, but perhaps not what you want. strtok returns on success line + some_offset so if later you need to
add more characters to vehicles[i].mode or vehicles[i].color, you might
overwrite memory. vehicles[i].color is only at an offset of
vehicles[i].model. If you even want to reallocate, realloc will fail,
because you wouldn't be reallocation at the beginning of the requested memory
block. Also by doing this you will lose the beginning of the requested memory,
it will leak memory, because you cannot free it (free(vehicles[i].color) is
not valid)1.
Another problem is that only the initial line woul have the correct amount of
allocated memory and if you call getline with a non NULL pointer and
non-zero length, getline will reallocate memory if necessary and update the
pointer and the length. If the reallocation returns the same address, then your
previous values are going to be overwritten. If the reallocation returns a
different address, you previous pointer will become invalid.
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
and after that do:
while (getline(&line, &len, fp)!= -1){
// the strtok calls
...
free(line);
line = NULL;
len = 0;
}
In this way your code won't leak memory and you would not overwrite memory.
edit
I should instead be setting the values of model and color with strcpy instead
You can use strcpy, but you would need to allocate memory first, because
model and color are just pointers. The malloc call only reserved memory,
it does not initialize it. So just doing
strcpy(vehicles[count].model, token);
would be wrong, because it you would try to copy something on an undefined
location. That's what I mean with
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
vehicles[count].model = malloc(strlen(token) + 1);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
strcpy(vehicles[count].model, token);
The function strdup essentially does that: malloc + strcpy, so if your
system has strdup you could do it like this:
vehicles[count].model = strdup(token);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
Another option would be to change your struct and instead of having pointers to
char, use char arrays:
struct data{
char model[100];
float engineSize;
int cost;
char color[100];
};
Now you can save strings with maximal length of 99 chars (which should be enough
for a model name and color) and just use strncpy instead, without the need of
extra memory allocation:
strncpy(vehicles[count].model, token, sizeof vehicles[count].model);
// making sure to terminate the string
vehicles[count].model[sizeof(vehicles[count].model) - 1] = 0;
Also I just haven't had a chance to change the code for free (line) line =null and len =0
I don't know what you mean by that. Just add the lines after
vehicles[count].color = token;
before the end of the while loop.
So then also I should be using the get line like I was in the second iteration through the file because I am currently over allocating?
The second loop is fine, the problem is that you are assigned the same (+
offsets) memory locations to different pointers and when getline reallocates
and gets a different address, the previous pointer becomes invalid. That's why
free(line);
line = NULL;
len = 0;
is important and you definitevly should do that.
To summerize: Your loop is fine, but you need to make these changes:
make copies of token or change the struct to use char arrays
add the
free(line);
line = NULL;
len = 0;
lines at the end of the loop and you'll be fine.
fotenotes
1vehicles[i].mode would only point at the beginning of the memory
block if and only if the line doesn't start with an empty space. As you are
reading a file, you don't have any guarantee that this is true. And even if it's
true, I wouldn't count on that. Better doing the safe thing here and make a
copy. free(vehicles[i].color) is definitely wrong.
Here is my full code, it looks like to work, but it's not working very well.
I would accept any code, that is working like this.
Firstly, the code works, but when I want to add the third name to the struct, it crashes.
Is there any other way to do this?
I need struct, because in the future, I want to add some other params, like age, average, gender, etc.
Please, help me out.
//The student table
typedef struct students {
char name[50];
} students;
//Global params
int scount = 0;
students *s;
//Basic functions
void addNewStudent();
int main()
{
int loop = 1;
char in;
int ch;
printf("Willkommen.\n Wahlen Sie bitte von die folgenden Optionen:\n");
while (loop)
{
printf("\t[1] Neue Student eingeben\n");
printf("\t[9] Programm beenden\n");
scanf(" %c", &in);
while ((ch = getchar()) != '\n');
switch (in)
{
case '1':
addNewStudent();
break;
case '9':
loop = 0;
break;
default: printf("------\nOption nicht gefunden.\n------\n");
break;
}
}
free(s);
return 0;
}
void addNewStudent()
{
int index = 0;
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
realloc(s, sizeof(students) * scount);
}
printf("Geben Sie Bitte die Name:\n");
fgets(s[scount].name, sizeof(s[scount].name), stdin);
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
scount++;
}
I'm using Visual Studio.
Thanks for help!
students *mynew= realloc(s, sizeof(students)* (scount+1));
if( mynew != NULL )
s=mynew;
Otehrwise you are having a memory leak. You didn't use the return value of realloc.
Don't cast the return type of malloc.
As per standard §7.22.2.35
void *realloc(void *ptr, size_t size)
The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by size.
It is good not to use the same pointer variable on which you are calling malloc because in case it fails you will lose reference to the old one too (unless it is stored by other means).
Also you didn't check the return value of malloc.
s = malloc(sizeof(students));
if( s == NULL ){
frpntf(stderr,"%s","Memory allocation failed");
exit(1);
}
Also you should check the return value of fgets().
if( fgets(s[scount].name, sizeof(s[scount].name), stdin) == NULL){
fprintf(stderr,"%s","Error in input");
exit(1);
}
Also trying to compile your code it showed this
warning: ignoring return value of ‘realloc’, declared with attribute warn_unused_result [-Wunused-result]
realloc(s, sizeof(students) * scount);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When compiling try not to ignore any warning messages. It showed the problem you had.
Important point: (why scount+1 in realloc?)
When reallocating the general idea is increase the number of students. And for that you need to have extra memory allocated for an student. That's why the scount+1 in the code.(realloc).
Some other points:
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
You can do it like this also
size_t len = strlen(s[scount].name);
if(len){
s[scount].name[len-1]='\0';
}
To understand why from standard §7.21.7.2
char *fgets(char * restrict s, int n,FILE * restrict stream)
The fgets function reads at most one less than the number of
characters specified by n from the stream pointed to by stream into
the array pointed to by s. No additional characters are read after a
new-line character (which is retained) or after end-of-file. A null
character is written immediately after the last character read into
the array.
\0 character was there already in the inputted string. You can get the length of it but you know that the one before the \0 is the \n character 1 that you entered by pressing the Enter key. We are overwriting it with the \0.
1. This is the usual case but not the only one. There are two cases where this might not be the right way to look at the thing.
The input line has n-1 or more characters before the '\n'. The the one before \0 will not be the \n rather it will be some character inputted by the user.
The last line is a stream which may not have a '\n'. (stdin closed). In that case also the input doesn't contain the \n.
So in these cases the idea of removing \n would fail.Discussed in comment. (chux)
A better and safe solution than overwriting this way:
s[scount].name[strcspn(s[scount].name, "\n")] = '\0';
The explanation from the link is that if a \0 is given as input then we will basically write to s[scount].name[SIZE_MAX] which is not desired.
From the standard §7.24.5.3
size_t strcspn(const char *s1, const char *s2)
The strcspn function computes the length of the maximum initial
segment of the string pointed to by s1 which consists entirely of
characters not from the string pointed to by s2.
How to correctly malloc a struct in C ?
p = malloc(sizeof *p);
if (p == NULL) Handle_OutOfMemory();
How to correctly re-allocate a struct in C ?
void *t = realloc(p, sizeof *p * number_of_elements);
if (t == NULL && number_of_elements > 0) {
Handle_OutOfMemory();
} else {
p = t;
}
p points to some struct. Notice no coding of that type in above.
OP' primary problem is not using the return value of realloc() and allocating 1-too-small
// realloc(s, sizeof(students) * scount);
s = realloc(s, sizeof *s * (scount+1)); // or use above code with check for out-of-memory.
realloc returns a new pointer that you need to keep:
students* snew = realloc(s, sizeof(students) * (scount + 1));
if (!snew) {
free(s); // If there is not enough memory, the old memory block is not freed
// handle out of memory
} else {
s = snew;
}
You are not allocating it back! Take a look at how realloc works. You need to assign the pointer back after making the re-allocation like this.
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
students *temp = realloc(s, sizeof(students) * (scount+1));
if(temp == NULL){
free(s);
}
else{
s = temp;
}
}
By Definition, realloc returns a void pointer but you aren't collecting it.
void *realloc(void *ptr, size_t size);
realloc returns a NULL if there's not enough space. So you can re-assign it when you are sure that it is not NULL
Just make a small change above and your code works like a charm!
Cheers!
When I ran a program, my console output showed the following:
malloc(): memory corruption (fast) and was followed by what seemed like an address in memory. I have narrowed it down to a few functions, but I feel that I free all memory that I allocate properly.
The following function takes a string representing a file name.
void readAndProcessFile(char* filename){
FILE *fileptr;
char* word = malloc(32*sizeof(char));
fileptr = fopen(filename, "r");
while(fscanf(fileptr,"%s",word) != EOF){
processWord(word);
}
fclose(fileptr);
free(word);
}
This function takes a word, removes any non-alphabetic characters and changes all letters to uppercase.
void processWord(char* text){
char* processedWord;
processedWord = trimAndCaps(text);
if(processedWord != NULL && processedWord[0] != '\0'){
addWord(processedWord);
}
free(processedWord);
}
Here's the trim and cap function
char* trimAndCaps(char* text)
{
int i = 0;
int j = 0;
char currentChar;
char* rv = malloc(sizeof(text));
while ((currentChar = text[i++]) != '\0')
{
if (isalpha(currentChar))
{
rv[j++] = toupper(currentChar);
}
}
rv[j] = '\0';
return rv;
}
And just for good measure here's the addWord function
void addWord(char* word)
{
// check if word is already in list
struct worddata* currentWord = findWord(word);
// word is in list
if(currentWord != NULL)
{
incrementCount(currentWord);
}
// word is not in list
else
{
currentWord = malloc(sizeof(struct worddata));
currentWord->count = 1;
strcpy(currentWord->word, word);
ll_add(wordList, currentWord, sizeof(struct worddata));
free(currentWord);
}
}
As you can see, all instances where I manually allocate memory, I free afterwards. This program works when there is a smaller amount of words, but not for larger. My thought process leads me to believe that there is some sort of leak, but when I have few enough words to the point that I can run it, I run the following code:
// The following code will print out the final dynamic memory used
struct mallinfo veryend = mallinfo();
fprintf(stderr, "Final Dynamic Memory used : %d\n", veryend.uordblks);
And this shows a 0 every time for memory used. What else can cause this? Any direction or fixes are much appreciated.
The following line doesn't do what you are hoping to:
char* rv = malloc(sizeof(text));
It only allocates 4 or8 bytes or memory depending on the size of pointers on your platform.
You need:
char* rv = malloc(strlen(text) + 1);
I'm currently writing a test program that parses input from a stream. I won't go into too much detail about this program but I am currently trying to parse alphanumeric characters and then assign them to a temp string, temp[100]. After all valid characters are assigned to temp, I allocate memory and strncpy to the allocated string variable.
Valgrind complains about my two usages of strlen and my single use of strncpy. Why is this? It complains about an uninitialised value but I made it clear that it won't do any allocation unless there are characters inside temp. Any suggestions?
char *name(char a)
{
int x;
char c;
char *returnName = 0;
char temp[100];
int i = 0;
/* Ensures no character is skipped */
temp[i] = a;
i++;
/* Fill temp one character at a time */
while((x = getchar()) != EOF)
{
c = (char)x;
/* Valid characters are assigned */
if((isalnum(c)) || c == '_')
{
temp[i] = c;
i++;
}
/* As soon as invalid character appears, exit loop */
else
break;
}
/* Make sure temp is not NULL before mallocing */
if(temp[0] != '\0') /* Thank you Alter Mann for this fix */
{
printf("Before malloc\n");
returnName = malloc(sizeof(char)*strlen(temp)+1);
printf("After malloc before strncpy\n");
strncpy(returnName, temp, strlen(temp)+1);
printf("After strncpy before return\n");
return returnName;
}
/* If nothing is assigned, return NULL */
return NULL;
}
You never null-terminated your string in temp, so both strlen() and strcpy() are reading past the initialized values in your array, hence the uninitialized value errors Valgrind is giving you.
Change:
char temp[100];
to:
char temp[100] = {0};
and you should be good.
Here:
if(temp != NULL)
You need to check
if(temp[0] != '\0')
temp is an array, not a pointer.
And (as pointed out by Paul Griffiths), NUL-terminate your string after the while loop:
temp[i] = '\0';