free malloc pointer locality issue - c

I am trying to implement a function set_it_free as shown below, but it would only play with the pointer locally, which does not do what I want, i.e. free and set to null for a pointer. Could you help me to modify my set_it_free to make it work?
int set_it_free(void* pointer, unsigned long level, char* message){
logMessage(level, message);
assert (pointer != NULL); //Never free a NULL pointer
free (pointer); //Free a malloc/calloc/realloc pointer
pointer = NULL; //Set it to NULL for prevention
return 0;}
I would call it like this in main()
int* a = (int* )malloc(sizeof(int));
set_it_free(a);

int set_it_free(void** ppointer, unsigned long level, char* message)
{
logMessage(level, message);
assert ( ppointer != NULL); //Check valid parameter
assert (*ppointer != NULL); //Never free a NULL pointer
free (*ppointer); //Free a malloc/calloc/realloc pointer
*ppointer = NULL; //Set it to NULL for prevention
return 0;
}
And example usage:
sometype* pMyVar = malloc(somesize);
set_it_free(&pMyVar, 5, "freeing myVar");

First of all, in C you should not cast the return values of functions returning void * (like malloc). It can cause subtle runtime problems if you forget to include the correct header files.
Secondly, when you pass the pointer pointer to the function, the pointer itself is passed by value, meaning the pointer is copied and any assignment to the local copy inside set_it_free will be only local. This means the assignment to NULL inside the function is only done to the local pointer variable, the pointer a will still point to the now unallocated memory once the function returns.
For the last problem there are four ways of solving this: The first is to pass the pointer by reference (as in the answer by abelenky), the other is to return the new pointer (similar to what realloc does). The third way is to assign to a after the function call, either NULL or make it point to something else. The fourth way is to simply not use the variable a again.

Related

Initialising struct through a function whose members are dynamic arrays

I have the following struct definition (names have been generalised):
typedef struct structure{
int *array1;
int *array2;
} structure_t;
I need to initialise this struct data structure through a function which takes in the sizes of the two arrays as parameters. The function returns a pointer to that data structure and NULL on fail.
I am getting confused on how to go about this. I know that I cannot return a pointer of a locally declared struct, and I also know that I need to dynamically allocate memory for the two members array1 and array2, since the size is not known on compile time (it's inputted by this user). I have tried the following:
structure_t* init(int size1, int size2)
{
structure_t *st = malloc(sizeof (structure_t));
if(st == NULL) return NULL;
st->array1 = malloc((sizeof (int))*size1);
st->array2 = malloc((sizeof (int))*size2);
return st;
}
I have checked and everything is being initialised. But then when I come to free the memory it is not working properly, as only the pointer to array1 is being changed to NULL.
bool destroy(strcuture_t *st)
{
free(st->array1);
free(st->array2);
free(st);
if (st == NULL)
return true;
else
return false;
}
What am I doing wrong?
free does not change the value of the pointer passed to it. It cannot, as it receives only the value and not a reference to the pointer.
To record that a pointer no longer points to valid memory, you can set the pointer to NULL yourself after calling free, as with:
free(st);
st = NULL;
Further, once the memory pointed to by st is freed, the C standard does not define the behavior of accessing st->array1 or st->array2 or even of using the value of st at all. There is no reason to expect that checking st->array1 or st->array2 for a null pointer will produce any particular result. The comparison may evaluate to true, may evaluate to false, may cause your program to abort, or may cause your program to misbehave in other ways.

C - Storing pointers with malloc() in an array, can't free() them afterwards

I want to store pointers that have been allocated using malloc() in an array and then free all of them after. However even though the program doesn't complain it doesn't work. Below cleanMemManager() won't actually free the memory as when tested inside main() the char* pointer is not NULL and it will print ???.
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void **ptrList = NULL;
void tfree(void** ptr)
{
free(*ptr);
*ptr = NULL;
}
void* talloc(int size)
{
void* ptr = malloc(size);
ptrList[0] = ptr; ///No clue if this actually does what I think it does
return ptrList[0];
}
void initMemManager()
{
ptrList = (void**)malloc(sizeof(void**) * 3);
memset(ptrList, 0, sizeof(void**) * 3);
}
void cleanMemManager()
{
tfree(&ptrList[0]); //Doesn't free the right pointer it seems
}
int main()
{
initMemManager();
char* ptr = (char*)talloc(3);
cleanMemManager();
if (ptr != NULL) //This will trigger and I'm not expecting it to
printf("???");
getchar();
return 0;
}
I don't understand the syntax to use for this, does the pointer not actually get touched at all? What is it freeing then since it doesn't throw any errors?
In main, char *ptr = (char*)talloc(3); declares ptr to be a local variable. It contains a copy of the value returned by talloc, and none of your subroutines know about ptr or where it is. So none of them change the value of ptr. Thus, when you reach if (ptr != NULL), the value of ptr has not changed.
Additionally:
In initMemManager, you should use sizeof(void *) in two places where you have sizeof(void**). In these places, you are allocating and copying void * objects, not void ** objects.
It looks like you are trying to implement a sort of smart memory manager that automatically sets pointers to NULL when they are freed. To do that in C, you would have to give up having copies of pointers. For example, ptr is a copy of ptrList[0], but tfree only sets whichever copy it is passed to NULL. We could give advice on building such a system, but it would quickly become cumbersome—your memory manager needs to keep a database of pointers and their copies (and pointers derived from them, as by doing array arithmetic). Or you have to refer to everything indirectly through that ptrList array, which adds some mess to your source code. Unfortunately, C is not a good language for this.
Freeing doesn't guarantee that pointers pointing to the allocated block will be set to NULL. If you actually try doing
if (ptrList[0] != NULL)
printf("ptrList[0] != NULL");
you will see that the program won't output and if you remove the cleanMemManager() function call, it will output. This means tfree function is working as intended, it's freeing the memory that was allocated.
Now as to why ptr variable being not set to NULL, it's simply because ptr is still storing the old address. cleanMemManager() has no way of mutating the variable ptr. This is commonly called dangling pointer or use after free.
Also free() doesn't clean/zero out the the allocated space, the block is simply marked as "free". The data will most likely remain in the memory for a moment until the free block is overwritten by another malloc request.

Safer way to realloc

I am implementing a function to safely realloc a structure in order not to lost the information if any allocation error occurs, something like this:
int foo (someStruct_t *ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(ptr, size);
if (tmp_ptr == NULL)
return -1;
ptr = tmp_ptr;
return 0;
}
My doubt resides in the following: Am I not duplicating the allocated memory for the structure everytime I run this function? In my line of thought I should free one of the pointers before exiting, correct?
The primary and major problem here is, after a call to foo(), in the caller, the passed argument for ptr will not be changed, as it itself is passed by value.
You need to pass a pointer to the pointer which you want to be reallocated, in case you don't want to return the new pointer.
That said, there is no "duplication" of memory here.
From realloc() point of view
realloc(), if successful, returns the pointer to the new memory and handles the job of de-allocating the older one. You don't need to worry about any duplication or memory leaks.
Quoting C11, chapter §7.22.3.5 (emphasis mine)
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. [....]
[....] If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
From the assignment point of view
A statement like ptr = tmp_ptr; does not duplicate the memory or memory contents the pointer points to, it is just having two copies of the same pointer. For example, You can pass either of them to free().
So, bottom line, to answer the "question" in the question,
In my line of thought I should free one of the pointers before exiting, correct?
No, you should not. You need to have the newly allocated pointer to be useful in the caller, free()-in it inside the called functions makes the whole function pointless. You should free the pointer from the caller, though.
int foo (someStruct_t **ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(*ptr, size);
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
}
the posted code contains several problems:
cannot change where a caller's pointer points without the address of that pointer, so use ** in the parameter list and call the function using: foo( &ptr, size);
each reference to the callers' pointer must now be dereferenced via a *
usually the size refers to the number entries room to be allocated, so size needs to be multiplied by sizeof( someStruct_t )
note the use of size_t for the size parameter because realloc() is expecting the second parameter to have the type: size_t
and now the proposed code:
#include <stdlib.h> // realloc()
int foo (someStruct_t **ptr, size_t size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc( *ptr, sizeof( someStruct_t) * size );
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
} // end function: foo

C - Struct Constructor and Deconstructor

If I have a struct like:
struct Cell
{
unsigned int *x, *y;
char flag;
};
Would the following constructor and deconstructors be sufficient for safely allocating and de-allocating memory?
// Constructor function.
struct Cell *Cell_new()
{
struct Cell *born = malloc(sizeof(struct Cell));
if (born == NULL)
return NULL;
born->x = NULL;
born->y = NULL;
born->flag = false;
return born;
}
// Deconstructor function.
// When called, use Cell_destroy(&cell);
char Cell_destroy(struct Cell **cell)
{
free(cell);
cell = NULL;
}
Is this correct?
One thing I don't understand is if I do:
struct Cell *myCell = Cell_new();
Cell_destroy(&myCell);
When I'm calling destroy, it is expecting an address to a pointer (a pointer to a pointer) and yet I'm providing an address to a struct.
What I mean is
My function expects:
Pointer -> Pointer -> Tangible Object
What I'm giving it:
Pointer -> Tangible Object
Is this flawed logic? I've been looking up this for awhile so it's safe to say I might be confusing myself.
It is correct to pass a parameter of type struct Cell ** to the destructor function because, this function will change the value of the pointer intended.
So, if you want to free the object and set the pointer to NULL, you can write:
void Cell_destroy(struct Cell **cell)
{
free(*cell);
*cell = NULL;
}
Note how cell is de-referenced.
I would consider the memory pointed to by the x and y pointers in the struct. If those pointers are non-null, what part of the code is responsible ("owns") that memory? A typical thing to do would be to check for null in Cell_destroy and if x or y is non-null, call free on them as well if it is the case that some other part of your Cell ADT (abstract data type) is allocating that memory dynamically via malloc. To be sure, free(cell) will only free memory used by the struct itself, not any memory pointed to by x and y.
Regarding Cell_destroy taking a pointer-to-a-pointer so it can set the passed-in pointer to null, that is an atypical way for a destructor function to work ... the way free itself works is more typical - nulling out the passed-in pointer is typically a job left for the caller of free.
Codes needs to free the same pointer it allocated. Free the de-referenced value.
Code is also missing a return value. Suggest simply using a void function.
Code should also free the field's pointers x and y.
Code should be tolerant of repeated calls with same pointer and a null pointer. Add if (*cell)
void Cell_destroy(struct Cell **cell) {
if (*cell) {
free((*cell)->x);
free((*cell)->y);
free(*cell);
*cell = NULL;
}
}
No, it is not correct.
The destroy function should not return char. It should have return type void.
Further the function should take a pointer instead of a double pointer and you should not set cell to null. And do not take address of myCell when calling destroy.

Initializing struct in C

OK, this is the definition of the struct:
typedef struct {
int first;
int last;
int count;
char * Array [50];
} queue;
and I use another function to initialize it
void initialize(queue * ptr){
ptr=malloc(sizeof(queue));
ptr->first=0;
ptr->last=0;
ptr->count=0;
}
Then I use printf to print out first, last and count. All three should be zero. However, what I actually get is, count is 0 as I expected, but first&last are two very large strange numbers and they change every time I run the program. Can anybody tell me what's wrong here? Thank you.
You are passing your pointer by value. The function changes a copy of the argument it receives, but the caller's pointer is not modified and is probably unintialized.
You need to change your function to take a queue** and pass the address of the pointer you want to initialize.
Alternatively you could return a pointer instead of passing it in as an argument. This is a simpler approach.
Given:
void initialize(queue * ptr);
Pass it like this:
queue q; // caller allocates a queue
initialize(&q);
// now q is initialized
Also, it's allocated by the caller -- don't malloc it.
// bad
void initialize_bad(queue * ptr){
ptr=malloc(sizeof(queue)); << nope. already created by the caller. this is a leak
ptr->first=0;
ptr->last=0;
ptr->count=0;
}
// good
void initialize_good(queue * ptr){
ptr->first=0;
ptr->last=0;
ptr->count=0;
// ptr->Array= ???;
}
If you prefer to malloc it, then consider returning a new allocation by using this approach:
queue* NewQueue() {
// calloc actually works in this case:
queue* ptr = (queue*)calloc(1, sizeof(queue));
// init here
return ptr;
}
Ultimately, what is 'wrong' is that your implementation passes a pointer by value, immediately reassigns the pointer to a new malloc'ed allocation, initializes the malloc'ed region as desired, without ever modifying the argument, and introducing a leak.
Here is the smallest alteration to your program which should correct your problem:
void initialize(queue * * pptr) {
queue * ptr;
ptr=malloc(sizeof(queue));
if (ptr) {
ptr->first=0;
ptr->last=0;
ptr->count=0;
}
/* The assignment on the next line assigns the newly allocated pointer
into memory which the caller can access -- because the caller gave
us the address of (i.e. pointer to) such memory in the parameter
pptr. */
*pptr = ptr;
}
The most important change is to pass a queue ** to your initialize function -- otherwise you are changing a copy of the queue * supplied as the actual parameter when you call initialize(). By passing a pointer to the pointer, you can access the original variable which stores the pointer in your caller.
I couldn't resist and also added a check for NULL returned from malloc(). That doesn't address your problem, but I couldn't bring myself to post code that didn't do it.

Resources