Reputation: 1467
I have a file "a", with 2000 characters, char "a" only, no spaces.
Then I have this code, that runs trough the loop, add it to buffer, eventually reallocs if the limit is reached and on errors it frees strBuffer variable.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int maxS = 200;
int numericExpression;
int strLength;
char *strBuffer;
strBuffer = malloc(sizeof(char)*maxS+1);
if(strBuffer == NULL)
{
printf("Failed to allocate requested memory!\n");
free(strBuffer);
strLength = sizeof(strBuffer);
printf("Freed %d bytes of memory!\n", strLength);
exit(99);
}
else
{
numericExpression = sizeof(char)*maxS+1;
printf("Alocated: %d Bytes of memory.\n", numericExpression);
}
// while simulation
int fcv = -1;
int numEx;
// file opening simulation
FILE* fd = fopen("a", "r");
int c;
while((c=fgetc(fd) != EOF)) // condition to make sure we realloc only once
{
fcv++;
strBuffer[fcv] = c;
if(fcv == (maxS))
{
printf("Additional memory space required!\n");
int strlensize = strlen(strBuffer);
numEx = (sizeof(char)*(2*strlensize));
strBuffer = realloc(strBuffer, numEx);
if(strBuffer == NULL)
{
printf("Failed to allocate requested memory!\n");
strLength = sizeof(strBuffer);
free(strBuffer);
printf("Freed %d bytes of memory!\n", strLength);
exit(99);
}
else
{
maxS = numEx;
printf("Reallocation successful!\n");
printf("Alocated: %d Bytes of memory.\n", numEx);
}
}
}
strLength = sizeof(strBuffer);
free(strBuffer);
printf("Freed %d bytes of memory!\n", strLength);
}
Problem is that it tells me in the end, that I freed only 8 bytes of memory. I suppose this is because sizeof(strBuffer) doesn't respond to the expected size. When I use strlen(strBuffer) instead, I free only 2001 bytes.
I suppose this could be only problem of printing out the amount of bytes freed. I could be doing it wrong. So maybe I'm just not able to tell how many bytes I free. But then I try valgrind and it tells me, I don't free enough, that there are memory leaks. But in every branch of a program, I get to free the memory used by strBuffer.
When I run it through valgrind ("valgrind ./realloc"), it tells me this:
==780== Memcheck, a memory error detector
==780== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==780== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==780== Command: ./realloc
==780==
Alocated: 201 Bytes of memory.
Additional memory space required!
==780== Invalid read of size 1
==780== at 0x4C2E0F4: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780== by 0x400846: main (in /home/dan/Desktop/test_ifj/realloc)
==780== Address 0x51fd109 is 0 bytes after a block of size 201 alloc'd
==780== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780== by 0x40078C: main (in /home/dan/Desktop/test_ifj/realloc)
==780==
Reallocation successful!
Alocated: 402 Bytes of memory.
==780== Invalid write of size 1
==780== at 0x400823: main (in /home/dan/Desktop/test_ifj/realloc)
==780== Address 0x51fd562 is 0 bytes after a block of size 402 alloc'd
==780== at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780== by 0x400866: main (in /home/dan/Desktop/test_ifj/realloc)
==780==
Additional memory space required!
Reallocation successful!
Alocated: 806 Bytes of memory.
Additional memory space required!
==780== Conditional jump or move depends on uninitialised value(s)
==780== at 0x4C2E0F8: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780== by 0x400846: main (in /home/dan/Desktop/test_ifj/realloc)
==780==
Reallocation successful!
Alocated: 804 Bytes of memory.
Freed 8 bytes of memory!
==780==
==780== HEAP SUMMARY:
==780== in use at exit: 568 bytes in 1 blocks
==780== total heap usage: 5 allocs, 4 frees, 2,781 bytes allocated
==780==
==780== LEAK SUMMARY:
==780== definitely lost: 0 bytes in 0 blocks
==780== indirectly lost: 0 bytes in 0 blocks
==780== possibly lost: 0 bytes in 0 blocks
==780== still reachable: 568 bytes in 1 blocks
==780== suppressed: 0 bytes in 0 blocks
==780== Rerun with --leak-check=full to see details of leaked memory
==780==
==780== For counts of detected and suppressed errors, rerun with: -v
==780== Use --track-origins=yes to see where uninitialised values come from
==780== ERROR SUMMARY: 1200 errors from 3 contexts (suppressed: 0 from 0)
How do I properly free memory I have allocated? So it won't cause memory leaks? Eventually - am I doing it wrong and there's a better way to do it? Thank you for your help.
I followed advises and got it to 3 errors in 1 context. This is how my code looks now:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int maxS = 200;
int numericExpression;
char *strBuffer;
strBuffer = malloc((maxS+1));
if(strBuffer == NULL)
{
printf("Failed to allocate requested memory!\n");
printf("Freed %d bytes of memory!\n", maxS);
exit(99);
}
else
{
numericExpression = sizeof(char)*maxS+1;
printf("Alocated: %d Bytes of memory.\n", numericExpression);
}
// while simulation
int fcv = -1;
int numEx;
// file opening simulation
FILE* fd = fopen("a", "r");
if(fd == NULL)
{
printf("Error opening a file!\n");
if(strBuffer != NULL)
{free(strBuffer);}
exit(99);
}
int c;
char *tmpBuffer;
while((c=fgetc(fd)) != EOF) // condition to make sure we realloc only once
{
fcv++;
strBuffer[fcv] = c;
if(fcv == (maxS))
{
printf("Additional memory space required!\n");
numEx = ((2*fcv));
tmpBuffer = realloc(strBuffer, numEx);
if(!tmpBuffer)
{
free(strBuffer);
printf("Realloc() failed!\n");
exit(99);
}
else
{
strBuffer = tmpBuffer;
}
if(strBuffer == NULL)
{
printf("Failed to allocate requested memory!\n");
printf("Freed %d bytes of memory!\n", maxS); // well this is questionable, I think
exit(99);
}
else
{
maxS = numEx;
printf("Reallocation successful!\n");
printf("Alocated: %d Bytes of memory.\n", maxS);
}
}
}
free(strBuffer);fclose(fd); // ADDED, still errors occur
printf("Freed %d bytes of memory!\n", maxS);
}
And with the same valgrind call ("valgrind ./realloc") I get this:
==1213== Invalid write of size 1
==1213== at 0x4007FD: main (in /home/dan/Desktop/test_ifj/realloc)
==1213== Address 0x51fd560 is 0 bytes after a block of size 400 alloc'd
==1213== at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1213== by 0x400831: main (in /home/dan/Desktop/test_ifj/realloc)
==1213==
Additional memory space required!
Reallocation successful!
Alocated: 800 Bytes of memory.
Additional memory space required!
Reallocation successful!
Alocated: 1600 Bytes of memory.
Additional memory space required!
Reallocation successful!
Alocated: 3200 Bytes of memory.
Freed 3200 bytes of memory!
==1213==
==1213== HEAP SUMMARY:
==1213== in use at exit: 568 bytes in 1 blocks
==1213== total heap usage: 6 allocs, 5 frees, 6,769 bytes allocated
==1213==
==1213== LEAK SUMMARY:
==1213== definitely lost: 0 bytes in 0 blocks
==1213== indirectly lost: 0 bytes in 0 blocks
==1213== possibly lost: 0 bytes in 0 blocks
==1213== still reachable: 568 bytes in 1 blocks
==1213== suppressed: 0 bytes in 0 blocks
==1213== Rerun with --leak-check=full to see details of leaked memory
==1213==
==1213== For counts of detected and suppressed errors, rerun with: -v
==1213== ERROR SUMMARY: 3 errors from 1 contexts (suppressed: 0 from 0)
Any tips what could cause this one?
Upvotes: 2
Views: 2571
Reputation: 16540
this compiles, and does the job
it includes error handling
it eliminated many meaningless variables
it eliminates the errors in the logic of the OPs code
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int maxS = 200; // current allocation size
char *strBuffer = NULL;
if( NULL == (strBuffer = malloc(maxS) ) )
{ // then, malloc failed
perror( "malloc failed" );
exit(99);
}
// implied else, malloc successful
printf("Alocated: %d Bytes of memory.\n", )maxS+1));
// file opening simulation
FILE* fd = fopen("a", "r");
if(fd == NULL)
{ // then fopen failed
perror( "fopen failed for file: a" );
free(strBuffer);
exit(99);
}
// implied else, fopen successful
int c; // receives input char from fgetc()
int fcv = 0; // index into malloc'd memory
// tmpBuffer used in realloc()
// so will not lose pointer to already allocated memory
// in case realloc() fails
char *tmpBuffer;
while((c=fgetc(fd)) != EOF)
{
strBuffer[fcv] = c;
fcv++;
if(fcv >= maxS)
{
printf("Additional memory space required!\n");
if( NULL == ()tmpBuffer = realloc(strBuffer, 2*maxS) )
{
perror( "realloc failed" );
free(strBuffer);
fclose(fd);
exit(99);
}
// implied else, realloc successful
maxS *= 2; // only update after being sure realloc successful
strBuffer = tmpBuffer;
printf("Reallocation successful!\n");
printf("Allocated: %d Bytes of memory.\n", maxS);
} // end if
} // end while
free(strBuffer);
fclose(fd);
printf("Freed %d bytes of memory!\n", maxS);
return( 0 );
} // end function: main
Upvotes: 1
Reputation: 918
Well, everybody up to now gave you really important advises and you should consider using them (mainly the tempBuffer
). BUT your problem is that you forgot to close your file descriptor:
fclose(fd);
Also, sizeof
is compile time, thus it cannot give you dynamic allocated memory size and strlen
needs a \n
character to work. Calculating allocated and freed memory is a hard task and its solution it's not so trivial.
I executed your updated code and I only got 1 error from 1 context, which can be solved by changing the following line:
tmpBuffer = realloc(strBuffer, numEx + 1);
Other than that, all memory, after fclose(fd), is free. I'm on a Ubuntu 12.04 machine using gcc 4.8.1.
Upvotes: 2
Reputation: 24587
This is your problem:
strBuffer = realloc(strBuffer, numEx);
If your call to realloc()
fails, then it returns a null pointer, but does not free the original memory allocation.
You need to check the return value first, then assign it to the original pointer if successful:
char *tmpBuffer = realloc(strBuffer, numEx);
if (!tmpBuffer) {
free(strBuffer);
puts("realloc() failed");
exit(1);
}
else {
strBuffer = tmpBuffer;
}
There are a few other issues with your code, including:
If strBuffer is NULL, then there is no point passing it to free().
sizeof()
is not a runtime function, so it has no idea how much memory was allocated.
strBuffer = malloc(sizeof(char)*maxS+1);
is a bit sloppy. I think you meant strBuffer = malloc(sizeof(char)*(maxS+1));
, but you could just have put strBuffer = malloc(maxS+1);
since sizeof(char)
is 1 by definition.
Upvotes: 3
Reputation: 141648
while((c=fgetc(fd) != EOF))
should be while((c=fgetc(fd)) != EOF)
. The result of this is that you're trying to store 1
in your string instead of the character you read.
The memory problem stems from strlen(strBuffer);
. You are calling strlen
on something that is not a null-terminated string, which causes undefined behaviour. (This shows up as the valgrind report saying "Invalid read of size 1 in strlen
).
To fix this, get rid of strlenSize
and do:
maxS = 2 * maxS;
strBuffer = realloc(strBuffer, maxS + 1);
Note that if you want a "clean" valgrind in the case when you ran out of memory, you will need to check the return value of realloc
before assigning it to strBuffer
, as squeamish ossifrage points out.
NB. Your error handling code is poor. sizeof(strBuffer)
finds the size of a pointer. The value you wanted to print is maxS
. Also, free(NULL)
has no effect; and there is no point having an else
block after a block in which you called exit()
.
Upvotes: 1