I'm a bit of a C newbie, so I'm still trying to get my head fully around when to worry about memory issues.
Suppose I have the following simple program:
#include <stdlib.h>
/* this returns a malloc()'d string */
char *get_str(int whichone);
int main(void)
{
char *s;
if ((s = get_str(0)) == NULL) {
exit(1);
}
/* position 1 */
if ((s = get_str(1)) == NULL) { /* position 2 */
exit(2);
}
return 0;
}
Obviously, this simple of a program has no worries about memory. It allocates a few bytes (for all intents and purposes), and it exits provided our dear function didn't fail.
Now, suppose I am running similar code inside of a looping and fork()ing program. Should I be using free(s) at position 1 since at position 2 I leave the old value behind and assign a new location for the pointer?
Yes. free releases the memory associated with the pointer that is being free'd. Once you reassign s so that it holds a different memory location, you will have lost your opportunity to free the memory associated with the original value of s.
You should definetly free the memory at position 1, because once you reassigned s you lost any chance to do so. And I'd do it one way or another because you can never know when somebody else is instructed to build a fork into your application and he will assume you did everything right.
If you have a pointer to memory that have been allocated using one of the malloc() type calls then when you are done with the memory area, you should deallocate the memory using free(). Similarly using calloc(), which also allocates from the heap, should be followed by the use free() to deallocate the memory when done with it.
The other memory allocator such as alloca() which allocates from the stack and not the heap see On the use and abuse of alloca does not use the free() function and that memory will be recovered automatically as the stack pointers are adjusted when the function in which it is used returns. Naturally that means that using a pointer from alloca() is only good for the function and any functions it calls and the address becomes invalid as soon as the function using alloca() returns.
In your simple example, I would use free() just before the call to the get_str(1) function at position 2 so the source would look something like"
int main(void)
{
char *s;
if ((s = get_str(0)) == NULL) {
exit(1);
}
// doing stuff with the string pointed to by s
/* position 1 */
// free up the string area so that we can get another one.
free (s);
if ((s = get_str(1)) == NULL) { /* position 2 */
exit(2);
}
return 0;
}
I might also be tempted to modify your example a touch and do something like the following. This was all written without testing in a compiler so there may be a compilation error or two however this should provide the basic idea.
The idea with this approach is to have a struct that contains state information as well as a pointer so that you will know which type of get_str() pointer came from, if you are interested, and when you call the get_str() function, it will deallocate the memory for you. You could also add intelligence so that if there is memory already allocated and it is the correct type, then you do not do a free() followed by a malloc() but instead just return back.
Another bonus provided by this approach is when a free() is done, the char * member of the struct is set to NULL which should give you a crash if you try to dereference the pointer and since the whichone member of the struct indicates the last type of get_str() used, your debugging may be easier depending on how good you are with interpreting a crash dump.
#include <stdlib.h>
typedef struct {
int whichone;
char *s;
} GetStrStruct;
/* this returns a malloc()'d string */
GetStrStruct *get_str(int whichone, GetStrStruct *pStruct)
{
free(pStruct->s); // we depend on pStruct->s being valid or NULL here
pStruct->s = NULL;
pStruct->whichone = -1;
switch (whichOne) {
case 0: // allocate string one
pStruct->s = malloc (32); // string one type of memory allocation
pStruct->whichone = whichone;
break;
case 1: // allocate string two
pStruct->s = malloc (64); // string two type of memory allocation
pStruct->whichone = whichone;
break;
default:
break;
}
// if the malloc() failed then return a NULL pointer
// we just reuse the pStruct pointer here, it is local and one less variable to make
if (pStruct->s == NULL) pStruct = NULL;
return pStruct;
}
int main(void)
{
GetStrStruct myStruct = {0}; // create and initialize the struct
if (get_str(0, &myStruct) == NULL) {
exit(1);
}
// do things with myStruct.s, the string from last get_str() call
// this would be using myStruct.s and not just myStruct or s so maybe awkward?
/* position 1 */
if (get_str(1, &myStruct) == NULL) { /* position 2 */
exit(2);
}
// do things with the second type of get_str()
// this would be using myStruct.s and not just myStruct or s so maybe awkward?
// release the memory as I am done. This may seem a bit strange to get a nothing string.
get_str (-1, &myStruct);
return 0;
}
If you added a debug facility you could even track which line of source did the last allocation and the last deallocation. Or if the malloc() fails, you could implement a debugger interrupt or other mechanism to immediately stop so you will know exactly which line the function call failed.
In the include file with the prototype for the get_str() function and the GetStrStruct struct you would use C Preprocessor as follows. If you change the 0 in the #if 0 to a 1 then the debug version is enabled otherwise it is not. The idea is to use a macro through the C Preprocessor to replace calls to get_str() with a call to get_str_Debug() and provide additional arguments with the source file path and the line number in the source file.
#if 0
typedef struct {
int whichone;
char *s;
struct {
int lineNo;
char file[64];
} myDebug;
} GetStrStruct;
GetStrStruct *get_str_Debug(int whichone, GetStrStruct *pStruct, char *file, int line);
#define get_str(wo,ps) get_str_Debug (wo,ps,__FILE__,__LINE__)
#else
typedef struct {
int whichone;
char *s;
} GetStrStruct;
GetStrStruct *get_str(int whichone, GetStrStruct *pStruct);
#endif
And then in the implementation file you could use the C Preprocessor with something like the following to allow you to specify whether the get_str() function should be replaced by a get_str_Debug() function with additional debug aids at compile time.
#if defined(get_str)
// provide a prototype for the replacement function signature.
GetStrStruct *get_str_Special(int whichone, GetStrStruct *pStruct);
// provide the debug information wrapper so that we can track where the last use came from
GetStrStruct *get_str_Debug(int whichone, GetStrStruct *pStruct, char *file, int line)
{
GetStrStruct *pTemp = get_str_Special (whichone, pStruct);
if (pTemp) {
// update the debug information. we keep only last 60 chars of file path.
int iLen = strlen (file);
if (iLen > 60) iLen -= 60; else ilen = 0;
strcpy (pStruct->myDebug.file, file + iLen);
pStruct->myDebug.lineNo = line;
} else {
// cause a debugger interrupt or a crash dump or something.
}
return pTemp;
}
GetStrStruct *get_str_Special (int whichone, GetStrStruct *pStruct)
#else
/* this returns a malloc()'d string */
GetStrStruct *get_str(int whichone, GetStrStruct *pStruct)
#endif
{
free(pStruct->s);
pStruct->s = NULL;
pStruct->whichone = -1;
switch (whichOne) {
case 0: // allocate string one
pStruct->s = malloc (32); // string one type of memory allocation
pStruct->whichone = whichone;
break;
case 1: // allocate string two
pStruct->s = malloc (64); // string two type of memory allocation
pStruct->whichone = whichone;
break;
default:
break;
}
// if the malloc() failed then return a NULL pointer
if (pStruct->s == NULL) pStruct = NULL;
return pStruct;
}
Then where ever you use get_str() if you turn on the debugging, the C Preprocessor will substitute a call to get_str_Debug() with the arguments used in the get_str() call along with two additional arguments, a char pointer to the source file name where the get_str() is being replaced by the get_str_Debug() and the source file line number.
Related
I'm new to C and trying to figure out how to dispose of the structures, references to which are returned from a function.
For example, this is roughly what I want to do.
typedef struct test_t{
char *test_c;
} test_t;
int testFunc(test_t** output){
test_t* testStruct = malloc(sizeof(testStruct));
char* buf = malloc(sizeof(char) * 5);
strcpy(buf, "test");
testStruct->test_c = buf;
*output = testStruct;
return 0;
}
int main() {
test_t* test;
testFunc(&test);
printf("%s\n",test->test_c);
free(test);
return 0;
}
So in main I get the test struct. After printf (assume that there is some code after wards) I no longer need it and want to deallocate it. But how to do it properly? Should I just deallocate test_c explicitly first? But what if it wasn't allocated?
A structure can be deallocated just like any other pointer. You however have to ensure that all its allocated members are freed before doing so (failing to do so would most likely result in a memory leak of the members of your structure).
typedef struct test_t{
char *test_c;
} test_t;
int testFunc(test_t** output){
test_t* testStruct = malloc(sizeof(testStruct));
char* buf = malloc(sizeof(char) * 5);
strcpy(buf, "test");
testStruct->test_c = buf;
*output = testStruct;
return 0;
}
int main() {
test_t* test;
testFunc(&test);
printf("%s\n",test->test_c);
free(test->test_c);
free(test);
return 0;
}
In case your structure doesn't allocate all of its elements, you can set their pointer to NULL, which once passed to free (3) would be equivalent to doing nothing.
But how to do it properly? Should I just deallocate test_c explicitly first? But what if it wasn't allocated?
Basic rule of thumb: if you write a function that allocates a resource, write a complementary one that deallocates it. So when applied to your example:
void testFree(test_t* toFree) {
free(toFree->test_c);
free(toFree);
}
... and then ...
printf("%s\n",test->test_c);
testFree(test);
If testFree is implemented and exposed along with testFunc, the callers don't need to know your implementation details. The functions themselves, being part of the same library, will make sure the invariants are maintained properly. Should you ever switch allocation schemes, no calling code needs to change. You would just modify testFree to go along with the new testFunc and re-link.
First, you'll need to free the memory you,ve allocated for the char in the struct given that you have initialized it. Otherwise, you'll get an error so before any free instruction you should check whether the pointer that holds the memory position for test_p you're trying to free isn't NULL. Besides, the free instruction should take *test as a parameter instead of test.
You should have a procedure which allocates and initializes an instance of test_t. In C++ or other object-oriented languages this would be a constructor. In C you have to roll your own - something similar to the following:
test_t *create_test_t(char *init_test_c)
{
test_t *tt;
tt = malloc(sizeof(test_t));
if(init_test_c != NULL)
{
tt->test_c = malloc(strlen(init_test_c)+1);
strcpy(tt->test_c, init_test_c);
}
else
tt->test_c = NULL;
}
You'll also want a function to get rid of a test_t instance correctly. Something like:
void destroy_test_t(test_t *tt, bool static)
{
free(tt->test_c);
if(!static)
free(tt);
}
The static parameter is included to control whether tt should or should not be free'd. Obviously you don't want to attempt to free a pointer to a static instance of test_t, in which case you'd pass TRUE for static. For dynamically-allocated instances of test_t, e.g. those created using the create_test_t procedure, you'd pass FALSE for static.
You then use these functions every time you need to create or destroy a test_t.
Best of luck.
Should I just deallocate test_c explicitly first?
Yes, you have to free test_c first, because, if you free test, you will lose the reference to test_c.
But what if it wasn't allocated?
As you are returning int from testFunc, you can use it as a state of the allocation of your structure (although, returning a pointer would be a better idea IMHO):
int testFunc(test_t** output){
test_t* testStruct = malloc(sizeof(testStruct));
if (testStruct == NULL)
/* testStruct allocation failure. */
return -1;
char* buf = malloc(sizeof(char) * 5);
if (buf == NULL) {
/* buf allocation failure. */
free(testStruct);
return -1;
}
strcpy(buf, "test");
testStruct->test_c = buf;
*output = testStruct;
return 0;
}
After that, you check testFunc failure:
/* Check for allocation failure. */
if (testFunc(&test) == -1) {
fprintf(stderr, "Allocation error");
exit(EXIT_FAILURE);
}
/* Allocation succeeded. */
printf("%s\n",test->test_c);
free(test->test_c);
free(test)
I want to make a program that dynamically allocates memory for each element of an array while it is entered from stdin and stored into an array. The reading should stop when 0 is entered. If I try to make it directly in main(), in looks like this:
int *a;
int i = 0;
a = malloc(sizeof(int));
do
{
scanf("%d", &a[i]);
a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
i++;
} while (a[i-1] != 0);
But I don't know how to make a function that does this. This is what I've tried, but it crashes everytime.
void read(int **a, int *cnt)
{
a = malloc(sizeof(int));
*a = malloc(sizeof(int));
*cnt = 0;
do
{
scanf("%d", a[*cnt]);
*a = realloc(*a, (*cnt + 2) * sizeof(int)); // enough space for storing another number
(*cnt)++;
} while (a[*cnt-1] != 0);
}
how about putting everything inside a function and returning a;
int *read()
{
int *a;
int i = 0;
a = malloc(sizeof(int));
if( !a ) return NULL;
do
{
scanf("%d", &a[i]);
a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
if( !a ) return NULL;
i++;
} while (a[i-1] != 0);
return a;
}
Assuming you are calling this in the usual way:
void read(int **a, int *cnt)
{
a = malloc(sizeof(int)); // This overwrites local a disconnecting it from the main a
*a = malloc(sizeof(int)); // so this will only change the memory pointed by local a and leak memory
...
}
int main()
{
int *a;
int cnt = 0;
read(&a, &cnt);
...
}
What is happening you’re giving the address to the pointer a to the function and then in the function you’re immediately overwriting it with the memory allocation. Matter this the a in the function and a in the main are completely separate entities. If you then allocate memory for *a you’re only storing that in the local a and the main a will remain pointing to whatever it happened to be. So it is uninitialized and causes undefined behavior.
So remove the line a = malloc(sizeof(int)) and your code will properly affect the main a also.
You also have to use *a for everything in read, including scanf and while. So it might be better to make the function handle allocation and return a pointer as was suggested in another answer.
Also note you should check realloc for return values so you won’t leak memory or crash there and you should use sizeof(int*) when allocating a pointer, no matter if size of int and int* were the same. It looks clearer.
You can pattern your function along the POSIX getline() function.
The pattern is very simple. Your function receives a reference to the pointer (i.e., a pointer to a pointer) used for the data, resized dynamically; and a pointer to the size allocated to that pointer. It will return the number of elements read to the array.
If you were reading doubles rather than ints, and wished to read all doubles from the input until end-of-input (either end of file, if redirected from a file, or until the user types a non-number and presses Enter, or until the user presses Ctrl+D at the beginning of the line), the code would look something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
size_t read_doubles(double **dataptr, size_t *sizeptr, FILE *in)
{
double *data; /* A local copy of the pointer */
size_t used = 0; /* Number of doubles in data */
size_t size; /* Number of doubles allocated for data */
/* Sanity checks against NULL pointers. */
if (!dataptr || !sizeptr || !in) {
errno = EINVAL; /* "Invalid parameter" */
return 0;
}
/* If *dataptr is NULL, or *sizeptr is zero,
there is no memory allocated yet. */
if (*dataptr != NULL && *sizeptr > 0) {
data = *dataptr;
size = *sizeptr;
} else {
data = NULL;
size = 0;
*dataptr = NULL;
*sizeptr = 0;
}
while (1) {
/* Ensure there is room in the data. */
if (used >= size) {
/* Need to allocate more.
Note: we have a copy of (data) in (*dataptr),
and of (size) in (*sizeptr). */
/* Reallocation policy. This one is simple,
reallocating in fixed-size chunks, but
better ones are well known: you probably
wish to ensure the size is at least some
sensible minimum (maybe a thousand or so),
then double the size up to a few million,
then increase the size in fixed-size chunks
of a few million, in a real-world application. */
size = used + 500;
/* Note: malloc(size) and realloc(NULL, size)
are equivalent; no need to check for NULL. */
data = realloc(data, size * sizeof data[0]);
if (!data) {
/* Reallocation failed, but the old data
pointer in (*dataptr) is still valid,
it isn't lost. Return an error. */
errno = ENOMEM;
return 0;
}
/* Reallocation succeeded; update the originals,
that are visible to the caller. */
*dataptr = data;
*sizeptr = size;
}
/* Read one more element, if possible.
Note: "&(data[used])" and "data+used"
are completely equivalent expressions.
*/
if (fscanf(input, " %lf", data + used) != 1)
break; /* No more input, or not a number. */
/* Yes, read a new data element. */
used++;
}
/* If we encountered a true read error,
return an error. */
if (ferror(input)) {
errno = EIO;
return 0;
}
/* Not an error; there just weren't more
data, or the data was not a number.
*/
/* Normally, programs do not set errno
except in case of an error. However,
here, used==0 just means there was no
data, it does not indicate an error per se.
For simplicity, because we know no error
has occurred, we just set errno=0 here,
rather than check if used==0 and only then
set errno to zero.
This also means it is safe to examine errno
after a call to this function, no matter what
the return value is. errno will be zero if no
errors have occurred, and nonzero in error cases.
*/
errno = 0;
return used;
}
The <errno.h> was included for the library to expose errno, and <string.h> for strerror(). These are both standard C.
However, the error constants I used above, EINVAL, ENOMEM, and EIO, are only defined by POSIXy systems, and might not exist in all systems. That is okay; you can just pick any smallish nonzero values and use them instead, because the function always sets errno. In that case, however, you need to check each of them and print the appropriate error message for each. In my case, all the systems I use define those three error codes for me, and I can just use strerror(errno) to convert the code to a standard error message (Invalid argument, Not enough space, and Input/output error, respectively, in non-localized programs).
Using a function defined like above, is very simple:
int main(void)
{
double *data = NULL; /* NULL for "Not allocated yet" */
size_t size = 0; /* 0 for "Not allocated yet" */
size_t used;
size_t i; /* Just a loop variable. */
used = read_doubles(&data, &size, stdin);
if (!used) {
/* No data read. Was it an actual error, or just no data? */
if (errno)
fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
else
fprintf(stderr, "No numbers in standard input!\n");
return EXIT_FAILURE;
}
printf("Read %zu numbers from standard input.\n", used);
printf("(The dynamically allocated array has room for %zu.)\n", size);
for (i = 0; i < used; i++)
printf(" %f\n", data[i]);
/* Array no longer needed, so we can free it.
Explicitly NULLing and zeroing them means
we can safely reuse them later, if we were
to extend this program. So, it's not necessary
to clear them this way, but it is a good practice
considering it makes long-term maintenance easier. */
free(data);
data = NULL;
size = 0;
used = 0;
/* This version of the program has nothing more to do. */
return EXIT_SUCCESS;
}
Essentially, you just set the pointer you supply the address of to NULL, and the size you supply the address of also to 0, before the call to indicate no array has been dynamically allocated yet. There is no need to malloc() an initial array; realloc(NULL, size) is completely safe, and does exactly what malloc(size) does. Indeed, I often write code that has no malloc() anywhere in it, and use only realloc().
Note that the above code snippets are untested, so there might be typos in them. (And I did choose to use doubles instead of ints and a different end-of-input condition, to ensure you don't just copy-paste the code and use as-is, without reading and understanding it first.) If you find or suspect you found any, let me know in a comment, and I'll check.
Also note that the above code snippets are long only because I tried to write descriptive comments -- literally most of the "code" in them is comments. Writing descriptive comments -- those that describe the intent of the code, and not just what the code actually does; the latter is easy to read from the code itself, but the former is what you or others later reading the code need to know, to check if the code is sound or buggy --, is very hard, and even after over two decades, I'm still trying to get better at it.
If you like writing code, I do warmly recommend you start practicing writing good, intent-describing comments right away, rather than battle with it for decades like I have. It is surprising how much good comments, and occasionally a good nights sleep to review the code with fresh pair of eyes, helps.
I'm creating a source files containing buffer functionality that I want to use for my other library that I'm creating.
It is working correctly but I'm having trouble getting rid of the buffer structure that I'm creating in one of the functions. The following snippets should help illustrate my problem:
C header:
//dbuffer.h
...
typedef struct{
char *pStorage;
int *pPosition;
int next_position;
int number_of_strings;
int total_size;
}DBUFF;
...
C source:
//dbuffer.c
...
DBUFF* dbuffer_init(char *init_pArray)
{
//Find out how many elements the array contains
int size = sizeof_pArray(init_pArray);
//Initialize buffer structure
DBUFF *buffer = malloc(sizeof(DBUFF));
//Initialize the storage
buffer->pStorage = malloc( (sizeof(char)) * (size) );
strncpy( &(buffer->pStorage)[0] , &init_pArray[0] , size);
buffer->number_of_strings = 1;
buffer->total_size = size;
buffer->next_position = size; //size is the next position because array allocates elements from 0 to (size-1)
//Initialize the position tracker which keeps record of starting position for each string
buffer->pPosition = malloc(sizeof(int) * buffer->number_of_strings );
*(buffer->pPosition + (buffer->number_of_strings -1) ) = 0;
return buffer;
}
void dbuffer_destroy(DBUFF *buffer)
{
free(buffer->pStorage);
free(buffer);
}
...
Main:
#include <stdio.h>
#include <stdlib.h>
#include "dbuffer.h"
int main(int argc, char** argv)
{
DBUFF *buff;
buff = dbuffer_init("Bring the action");
dbuffer_add(buff, "Bring the apostles");
printf("BUFFER CONTENTS: ");
dbuffer_print(buff);
dbuffer_destroy(buff);
// Looks like it has been succesfully freed because output is garbage
printf("%s\n", buff->pStorage);
//Why am I still able to access struct contents after the pointer has been freed ?
printf("buff total size: %d\n", buff->total_size);
return (EXIT_SUCCESS);
}
Output:
BUFFER CONTENTS: Bring the action/0Bring the apostles/0
��/�
buff total size: 36
RUN SUCCESSFUL (total time: 94ms)
Question:
Why am I still able to access struct contents using the line below after the pointer to the struct has been freed ?
printf("buff total size: %d\n", buff->total_size);
Once you've called free() on the allocated pointer, attempt to make use of the pointer invokes undefined behavior. You should not be doing that.
To quote C11 standard, chapter §7.22.3.4, free() function
The free() function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. [..]
It never say's anything about a cleanup, which you might be (wrongly) expecting.
Just to add clarity, calling free() does not always actually free up the allocated physical memory. It just enables that pointer (memory space) to be allocated again (returning the same pointer, for example) for successive calls to malloc() and family. After calling free(), that pointer is not supposed to be used from your program anymore but C standard does not guarantee of a cleanup of the allocated memory.
If any attempt is made to read memory that has been freed can crash your program. Or they might not. As far as the language is concerned, its undefined behaviour.
Your compiler won't warn you about it(or stop you from accessing it). But clearly don't do this after calling free -
printf("buff total size: %d\n", buff->total_size);
As a good practice you can set the freed pointer to NULL .
free() call will just mark the memory in heap as available for use. So you still have the pointer pointing to this memory location but it's not available anymore for you. Thus, the next call to malloc() is likely to assign this memory to the new reservation.
To void this situations normally once you free() the memory allocated to a pointer you should set it to NULL. De-referencing NULL is UB also but at least when debugging you can see tha pointer should not be used because it's not pointing to a valid memory address.
[too long for a comment]
To allow your "destructor" to set the pointer passed to NULL modify your code like this:
void dbuffer_destroy(DBUFF ** buffer)
{
if ((NULL == buffer) || (NULL == *buffer))
{
return;
}
free((*buffer)->pPosition);
free((*buffer)->pStorage);
free(*buffer);
*buffer = NULL;
}
and call it like this:
...
dbuffer_destroy(&buff);
...
I'm working on a C project (assignment for school). One of the demands is that in case of malloc() failure, the program must free() all allocated memory and exit().
Consider a case where function A() constructs a linked-list and in each iteration it calls to another function, B(). Now, if a malloc failure occured at B(), it must free() the memory it allocated but function A() should do that as well.
Things are getting quite complicated when you have a tree of function calls larger than two.
In my previous project I used a flag to notify a malloc() failure - if a function uses another function which may use malloc(), it has to check the flag right after. It worked, but code got kinda messy.
Is there a neat solution for this problem?
Of course, with "real" applications all memory is de-allocated by the OS, but I guess this demand is pedagogical..
I think the easiest approach is to create a custom allocator (as somebody already noted in a deleted post) to keep track of all your allocations, then do a custom deallocator, use these for all your heap memory needs.
if a malloc fails you have the list of previously allocated blocks at easy reach.
e.g.
(you need to redo this cause it is not effective and should be optimized but shows the principle and only ocular compilation)
typedef struct
{
void* pMemory; /* for the allocated memory */
size_t size; /* for better debugging */
} MemoryBlock;
#define MAXBLOCKS 1000
MemoryBlock myheap[MAXBLOCKS]; // global so zero:ed
static int block = 0;
void* myalloc(size_t size)
{
static int block = 0;
// you should check vs MAXBLOCKS
myheap[block].pMemory = malloc(size);
myheap[block].size = size;
// check if it failed.
if ( myheap[block].pMemory == NULL )
{
for (int i = 0; i < block; ++i)
{
myfree(myheap[i].pMemory);
}
fprintf( stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
else
{
return myheap[block++].pMemory;
}
}
void myfree(void* p)
{
for (int i = 0; i < block; ++i)
{
if ( p == myheap[i].pMemory )
{
free(myheap[i].pMemory);
myheap[i].pMemory = NULL;
return;
}
}
}
Yes. The best (and conventional) way is to initialize every pointer value to zero. Then set it during the malloc() assignment. Ex: myPtr = malloc( 10 );
It will be zero in case of failure, and you check that. And finally, when you go about freeing, you always check the pointer value before calling free():
if ( myPtr != 0 )
free( myPtr );
There is no need for an extra flag.
Are you having issue checking for errors or handling them? If you want info on catching them, use donjuedo's suggestion.
For ideas on freeing memory in the event of error, try one of these two methods:
1) For a uni-directional linked-list, keep a special pointer that points to the head of the list. In your cascading free function, start at the head, capture the next-pointer in a temp variable, free the head, move to the next structure in the list using the temp-pointer, and repeat the process until the next-pointer == 0.
2) For a bi-directional linked-list (my preference) you don't need to keep a special pointer to the head of the list. Assuming you are still at the tail, just capture the previous-pointer into a temp variable, free the tail, move back using the temp-pointer, and repeat the process until the previous-pointer == 0
You could look into the atexit() function, to register code that will be executed when the program terminates. Such code can then check if there is anything that needs to be free()d.
Note that atexit() has no way to unregister. So you need to make sure that you register each cleanup function only once, and that it does the right thing when there is nothing to clean up.
#include <stdlib.h>
#include <stdio.h>
int *ptr1;
char *ptr2;
int clean1_registered, clean2_registered;
void clean1(void)
{
printf("clean1 called\n");
if (ptr1) {
free(ptr1);
ptr1 = NULL;
}
}
void clean2(void)
{
printf("clean2 called\n");
if (ptr2) {
free(ptr2);
ptr2 = NULL;
}
}
void B(void)
{
ptr2 = malloc(100);
if (!clean2_registered) {
atexit(clean2);
}
}
void A(void)
{
ptr1 = malloc(100 * sizeof(int));
if (!clean1_registered) {
atexit(clean1);
}
B();
}
int main(int argc, char **argv)
{
A();
}
Just for fun (and for C programming practice) I wrote the following piece of code that does the following:
Acts as a tracking system for memory allocations
Frees all dynamically allocated memory with a function call
Here is the code:
typedef enum _OpMode {
OM_APPEND,
OM_DESTROY
} OP_MODE;
void refOp(void *ptr, OP_MODE mode) {
/* contains static array of pointers and provides an interface to that
array */
static void **references = NULL;
static int size = 0;
static int reset = 0;
if (reset) {
reset = 0;
references = NULL;
size = 0;
}
switch (mode) {
case OM_APPEND:
//add a pointer to reference array
references = (void**) realloc(references, sizeof(void*) * (size + 1));
references[size++] = ptr;
break;
case OM_DESTROY:
//free memory at all pointers kept in reference array
for (int i = 0; i < size; i++) {
free(references[i]);
references[i] = NULL;
}
free(references);
reset = 1;
break;
default:
printf("Invalid enum value '%d' passed as mode.\n", mode);
break;
}
}
void refDestroyAll() {
//Wrapper function
refOp(NULL, OM_DESTROY);
}
void *myAlloc(void* ptr, size_t size) {
/* Allocates memory and stores pointer copy in reference array */
void *tmp_ptr;
tmp_ptr = realloc(ptr, size);
refOp(tmp_ptr, OM_APPEND);
return tmp_ptr;
}
The idea is that one would use myAlloc() instead of malloc or realloc to dynamically allocate memory. And one would use refDestroyAll() to free all memory that was created with myAlloc().
I've done some testing, and it seems to be working, but I can't help feeling that I'm missing something important. Does this code actually work as intended, or am I leaking memory when I call refDestroyAll()?
You have a bug, that could cause a segmentation fault. realloc() could return the same pointer as it is given, in which case you would have added it twice to the array. When you call your free function, it would try and free the same pointer twice, resulting in a segmentation fault error.
Additionally, I don't understand why you have the reset parameter. Why not simply set references and size to 0 in the OM_DESTROY case? It is good practice to always set a pointer to NULL immediately after freeing it.