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.
Related
I have a struct in my main function. I pass that pointer to another function which does some stuff and if conditions are met, it passes it to another function to get filled out. When returning to the main function the t struct contains none of the data mydata that was copied into it.
typedef struct _t {
int one;
int two;
int three;
int four;
} T;
void second(T *t) {
t = malloc(20);
memcpy(t, mydata, 20);
}
void first(T *t) {
second(t);
}
int main() {
T t;
first(t);
}
Do I need to be working with double pointers here? If the address of t was 0x1000 and I passed it to first() then wouldn't referencing t just be 0x1000? And same as if I pass the pointer to second()?
In this answer, I assume that, for reasons not shown, you do in fact need to make a dynamic memory allocation. If that is not the case, the only changes that need to be made are replacing first(t); with first(&t);, and removing t = malloc(20);.
The first problem to fix is that t in main should have the type T *, not T. You are making a dynamic memory allocation, and seem to want to store that pointer in t, so you would need: T *t;.
The second problem is that you want to manipulate the value of t in main, but are passing it by value to first. Instead, you need to pass a pointer to t into first: first(&t);.
Fixing both of these, you now pass a pointer to a pointer to T (the type of &t) into first and second, so you need to change their signatures to be, respectively, void first(T **t) and void second(T **t).
Applying both changes, as well as making some small style tweaks, we get:
typedef struct T {
int one;
int two;
int three;
int four;
} T;
void second(T **t_ptr) {
*t_ptr = malloc(20);
memcpy(*t_ptr, mydata, 20);
}
void first(T **t_ptr) {
second(t_ptr);
}
int main() {
T *t;
first(&t);
}
Another thing that's missing, and needs to be added, is checking for the success of malloc, but I haven't added that to the above code.
Also, what you've shown in the question shouldn't compile; you're passing a struct to a function that accepts a pointer.
Your problems are common to new C developers. And actually you have two of them.
The first problem is that you pass your structure by value.
The first function is declared to receive a pointer to T but you pass t and not &t (which is the address of t - and this is what you want when a function accepts a pointer).
However there is still another problem so that even if you change your code as suggested above it will still not work correctly. second allocates memory using malloc. The function receives T as a pointer T *t. You assign the output of malloc to t in effect overwriting what t points to (and if t was previously allocated you will leak memory here).
Bellow you can see a correct code for what you want.
typedef struct _t {
int one;
int two;
int three;
int four;
} T;
/* Make sure we have some data to initialize */
T mydata = {0};
/*
We take a pointer to a pointer and change what the external pointer points to. */
In our example when this function is called *ppt is NULL
and t is a pointer to t in main()
*/
void second(T **ppt) {
/*
We never calculate the size of structures by hand. It can change depending on
OS and architecture. Best let the compiler do the work.
*/
*ppt = (T*)malloc(sizeof(T));
memcpy(*ppt, &mydata, sizeof(T));
}
void first(T **ppt) {
/* Make sure we don't leave dangling pointers. */
if (NULL != *ppt)
free(*ppt);
second(ppt);
}
int main() {
T *t = NULL; /* A pointer to our data */
/*
We pass a pointer to our pointer so that the function can change the value it
holds
*/
first(&t);
/* Always do an explicit return if the type of the function is not void */
return 0;
}
How to understand what is going on:
First we declare t as a pointer to a memory holding a type T and we make sure we initialize the pointer to point to NULL (which is a convention meaning that the pointer is not initialized).
We have a function that will allocate the memory for us using malloc. malloc allocates memory from the heap and returns the address of that memory. (In reality a pointer is just a variable holding an address in memory). We want to place that address in t declared in main(). To do so we need to pass to the allocating function the address of t so it can be modified. To do this we use the address of operator - &. This is why we call the function like this first(&t).
Our allocating function accepts a pointer to a pointer. This is because we want to change the address t points to. So we declared the parameter as T **ppt. It holds the address of the pointer *t in main. In the function we dereference the pointer to the pointer to get the original pointer we want to assign the address malloc returns.
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.
so I'm trying to understand the whole concept of memory management in C and I was given this code:
int main(int argc, int *argv[]) {
item *x = NULL;
x = (item *) malloc (sizeof(item));
...
free_item(&x);
}
void free_item(item **x) {
free(*x);
*x = NULL;
}
where item is earlier defined structure. The point, where I get confused is free_item(&x); because when I write free_item(x); and change the function to:
void free_item(item *x) {
free(x);
x = NULL;
}
the code seems to work the same way as the previous one.
So, is there any difference? And if not, is there any reason, why would someone send an adress on a pointer of a structure to a function, which frees this structure?
Yes. First, I think there's a typo in your modified function. It should be:
void free_item(item *x) {
free(x);
x = NULL;
}
Now, for the differences. The free call will succeed, as all it expects is a "pointer-to-type" variable for data which is allocated dynamically on the heap.
The second part, x = NULL, will not work as expected. Remember, in C, when passing an argument to a function, we pass by value always, never by reference. You are being passed a copy of a variable, so x = NULL just sets a temporarily automatically allocated copy of x to NULL, not the actual variable passed as an argument.
The original function which you changed does both parts correctly.
In the first case, to modify the content of pointer variable x, you need to pass on using a reference. That's why the &x and the double pointer is being used to free() the memeory which has been allocated from main(). This is correct.
in second case, a function-local copy of x will be created and passed on to free_item() which will have no impact on the x present in main(). The x = NULL; will have the scope only inside free_item().
In your first example you are calling free_item with argument of type pointer to pointer to item (assuming item **x argument).
In second example point value (main::x) is copied to free_item::x (different scope) and that copied value is set to NULL (and value is discarded afterwards).
Add printf("%p\n", x); to the end of main() (after free_item).
With first example you will see NULL and with second x will still be set to address previously stored there.
In first example, lets say your pointer is on address 0x00400000 and it points to memory allocated at 0x00410000.
0x00400000 0x00410000 <- x is stored here
0x00400004 0x00400000 <- tmp described later; point to x
...
0x00410000 .......... <- x points here; start of item - returned by malloc
When you call item **tmp = &x you will see that it will contain the address 0x00400000 (and tmp itself would be stored on different memory address).
Then in free_item():
free(*tmp); // The same as free(0x00410000)
*tmp = NULL; // Modifies data at 0x00400000 = NULL
When you use free ,you pass the pointer ,not the value of the pointer.
So, if you use
item *x
, you should use
free(x)
x = (item *) malloc (sizeof(item)); is unnecessary. x = malloc(sizeof(item)); will suffice.
2.
void free_item(item *data){
free(data);
}
Is the correct way to do it.
Write idiomatic C code. There is no difference perse but its hard do debug and maintain.
I would write free_item(x); rather than free_item(&x) and have free_item(item **x)
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.
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.