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.
Related
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.
Recently, I'm learning C. I found a question on the internet. The question is:
What is the problem with this function in terms of memory allocation?
What is a good solution? You may assume that a struct item type has
been declared. The purpose of this function is to allocate an array of
struct item, which you may assume has been declared prior to this
function.
BOOLEAN allocate_items(struct item * items, size_t howmany)
{
size_t count;
items = malloc(sizeof(struct item) * howmany);
if(!items) {
perror("failed to allocate memory");
return FALSE;
}
return TRUE;
}
So, I think that the 4th line is wrong. It should be like:
items = malloc(sizeof(struct item));
And also the 6th line is wrong. It should be like:
if(items == NULL){
Is it correct?
First of all, both line 4 and 6, as you mentioned seems to be OK.
That said, the basic problem with this function is, you're allocating memory to a local scope of variable. This way
as you don't return the pointer to allocated memory, after the function returns, there would be no way to access the allocated memory.
by not freeing up the allocated memory, you will face memory leak.
If you have to allocate memory to a pointer, you need to pass the address of that pointer to the function and allocate memory. You can also return the pointer but then you need to change the function signature.
Finally, arrays are not pointers and vice-versa. They may appear or beahave similar sometimes, but they are not the same.
The 4th line is not wrong as they are trying to declare an array of the structs.
You should add a line inside the function that declares a new pointer, temp, to hold the current value of items, then after allocating the memory,
the 6th line should be
if(items == temp)
to check whether the value has changed(because that is the closest we can get to checking whether malloc worked)
this is because the ! operator is used to check if a condition is true or not(at least at a basic level in most languages) and as a pointer isn't a condition or an int that can be used as true or false, the operator won't work.
Here a fixed version, as it would probably be written in the "industry".
bool allocate_items(struct item ** pitems, size_t howmany)
{
// argument validation
assert(NULL != pitems); // some also add release version checks...
if(NULL == pitems ) return false;
// We can also spot memory leak sources here.
// If *pItems != NULL - does that mean we have to free first to prevent
// a leak? What if it is just some random value and not something we can
// free? So contract usually is: *pitems has to be NULL...
assert(NULL == *pitems);
if(NULL != *pitems) return false;
// implementation
*pitems = malloc(sizeof(struct item) * howmany);
if(NULL == *pitems) {
perror("failed to allocate memory");
}
return NULL != *pitems;
}
While the bool defined in stdbool.h sometimes causes trouble with C++ interop (same symbols on both sides, but sometimes sizeof(bool) differs), it is still the better option compared to inventing yet another bool type.
The pitems is a pointer to the location where the pointer to the new chunk of memory shall be written to. A caller of this function might have written:
int main(int argc, const char*[] argv) {
struct item *myBunchOfStuff = NULL;
if(false != allocate_items( &myBunchOfStuff, 20) ) {
// ...
free(myBunchOfStuff);
myBunchOfStuff = NULL;
}
return 0;
}
Defensive programming states: Your function cannot claim "Heh - my function only crashed because I was given a bad value!". Instead, it has to verify itself. It is responsible not to crash. The pointer could still be != NULL but otherwise bad. That is impossible for the function to catch, usually.
In C, everyone is proud of not requiring the cast of malloc()'s return value. You can be proud of that until you compile your code with a C++ compiler. Then you have to change your code and fix that. Well, I guess it is a matter of preference...
While parameter checking is often seen as a separate part of the functions implementation, after that, you should try to stick to "single point of exit". Main reason for that is maintainability. With multiple exit points, if the function gets bigger later on, it gets harder to spot if some early exit forgets to free some memory or cleanup other forms of state.
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.
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.
I got a problem with checking whether a member of a struct is null or not. For example:
typedef struct {
int value;
} A;
int main() {
A *foo = malloc(sizeof(A));
foo->value++; // obviously, null pointer error
}
Then I attempted to check whether value is NULL or not by:
if (foo->value != NULL) {
foo->value++;
}
But the error occured: comparison between pointer and integer.
What should I do? Note that set it to 0 is not an option in my circumstance.
You probably meant
if (foo)
foo->value++;
Also your comment "obviously null pointer error" is not so obvious to me.
foo->value++; // obviously, null pointer error
Not so obvious to me why this is an error. value is an int, not an int *, so how can it ever be a NULL pointer?
I think you meant to perform the following check
A *foo = malloc(sizeof(A));
if( foo != NULL ) {
foo->value++; // increment value
} else {
// allocation failed, do something about it
}
When you malloc a structure enough memory is allocated to contain all members of that structure; you do not have allocate memory for every single member.
Note that the contents of the structure itself are uninitialized after the malloc call. To set the members to a deterministic state you can do any one of the following:
foo->value = 0; Similarly initialize all other members
memset( foo, 0, sizeof(*foo) ); This sets all bits within the structure to zeros
A *foo = calloc( 1, sizeof(A) ); calloc automatically zero initializes the allocated memory
An int isn't a pointer. It can't be NULL (or at least that value is indistinguishable from zero).
That's an int, not a pointer, so it doesn't make much sense to compare it to NULL. If you want to be able to do that, then you need to declare it in the struct as an int*. Otherwise just make sure you initialize it to 0 when you create the struct.
To expand on the above, it's important to be aware that when you malloc() that struct, the space for that int is ALREADY IN the space provided by the malloc(). The struct literally contains the int, not just a pointer to it.
The int type is not a pointer so it can not be a NULL pointer. When you create a struct enough room is allocated for your values but you still need to initialize its contents, otherwise they will be set to what was previously in the allocated space, and sometimes this best done using another function.
A *createA()
{
A *a = malloc(sizeof(A));
//TODO: make sure a is no null
a->value = 0;
return a;
}
Calling createA will now always return a struct with the int value set to 0.
As others have pointed out, int is not a pointer type and cannot be NULL. However, I believe what you were trying to get at by the 'obviously, null pointer error' remark was that memory obtained from malloc is not initialized, so foo->value++ won't necessarily leave you with 1. To fix this you can either use calloc or memset:
calloc:
int main() {
A *foo = calloc(1, sizeof(A)); // returns 0'd memory
foo->value++;
}
memset:
int main() {
A *foo = malloc(sizeof(A));
memset(foo, 0, sizeof(*foo)); // sets the contents of the struct to 0
foo->value++;
}
You can also manually 0 the fields, which works fine, but you leave yourself open to forgetting to zero new fields you add to the struct later.