So, I'm working in C and I'm allocating an array of type struct, and within that struct I'm also allocating another array of different struct.
typedef struct {
char name[MAX_NAME + 1];
ITEM *items;
} database;
typedef struct {
char name[NAME_MAX + 1];
}ITEM;
database *array = (database*)malloc(n1 * sizeof(database));
array[i].items = (ITEM*)calloc(n2 * sizeof*(ITEM));
Does the command free(array); take care of freeing the whole thing, including the ITEM array I allocated within it?
No, you need to free every single data allocation done by calloc() or malloc().
A few points
calloc(n2 * sizeof*(ITEM)); is completely wrong. You need write like this
array[i].items = calloc(n2, sizeof(ITEM));
You don't need to typecast the value returned from malloc() and calloc()
Always check whether heap-allocation was successful or not, by checking the pointer against NULL
struct ITEM should be declared before struct database, to avoid forward-declaration error
You need to free the resources using a loop which will iterate till n1
for (size_t i = 0; i < n1; i++)
{
free(array[i].items);
}
free(array);
Related
I am studying C (self-study, not in an educational institution) and have been trying to build a hashtable data structure as part of my learning.
Please refer to this hopefully reproducible example:
#include <stdio.h>
#include <stdlib.h>
struct table_item {
char *name;
char gender;
char *birthdate;
char *address;
};
struct list_node {
struct table_item *data;
struct list_node *next;
unsigned long long hash_key;
};
struct hashtable {
int table_size;
int num_entries;
struct list_node **entries;
};
struct hashtable* init_hashtable(int size);
void free_hashtable(struct hashtable *table);
int main(void)
{
struct hashtable *hashtable = NULL;
int size_entry = 0;
printf("Input hashtable array size: ");
while (size_entry < 1) {
scanf(" %d", &size_entry);
}
hashtable = init_hashtable(size_entry);
free_hashtable(hashtable);
return 0;
}
struct hashtable* init_hashtable(int size) {
struct hashtable* new_table;
if ((new_table = malloc(sizeof(struct hashtable))) == NULL) {
perror("Error: failed to allocate memory for hash table\n");
exit(EXIT_FAILURE);
}
new_table->table_size = size;
new_table->num_entries = 0;
if ((new_table->entries = malloc(size*sizeof(struct list_node))) == NULL) {
perror("Error: failed to allocate memory for hash table array\n");
exit(EXIT_FAILURE);
}
return new_table;
}
void free_hashtable(struct hashtable *table) {
for (int i = 0; i < table->table_size; i++) {
if (table->entries[i] != NULL) {
free_list(table->entries[i]);
table->entries[i] = NULL;
}
}
free(table->entries);
free(table);
}
My issue is that trying to free the table always fails, even if I have not added anything to it.
I used GDB to check the issue. It seems that, in the above for loop, if (table->entries[i] != NULL) always fires (such as when i=0) even when I haven't added anything. This results in my free_list function trying to free inappropriate memory, which is why I get the stack dump.
Somehow it seems that table->entries[i] is actually not NULL but rather has a struct list_node * type, causing the if condition to fire inappropriately. Could somebody please explain to me why this is?
I was hoping that I could use this for loop to go through the entries array and only free memory where malloced nodes exist, but as it stands this will just crash my program. I am not sure how I can alter this to behave as I'd like it to.
Somehow it seems that table->entries[i] is actually not NULL
Indeed, because you never initialized it to NULL.
init_hashtable allocates space using malloc and points table->entries. Now malloc does not initialize the memory it provides. Its contents are garbage, and in particular, there is no reason why it should consist entirely of NULL pointers as your code expects.
If you want table->entries to be full of NULL pointers then you have to explicitly initialize it, either with a loop, or with memset(entries, 0, size*sizeof(struct list_node *)). Or best of all, by calling calloc instead of malloc, which also avoids bugs in case the multiplication size*sizeof(struct list_node *) overflows.
(Technically memset and calloc initialize memory to all-bits-zero, which in theory does not have to correspond to NULL pointers, but it actually does on all systems you are likely to encounter. But to be pedantic, the loop is the only strictly conforming way to do it.)
but rather has a struct list_node * type,
This has nothing to do with types. Types in C are statically determined from declarations, and there is no way for an object to have an unexpected type at runtime. The type of table->entries[i] is struct list_node * no matter what. The question is about the value of that object; you expect it to be NULL but it's not. "Null pointers" are not a separate type in C; NULL is simply a value that a pointer of any type may have.
As Avi Berger points out, there is another bug in that the size calculation in the malloc should be size*sizeof(struct list_node *) not sizeof(struct list_node). Each element is not a struct list_node but rather a pointer. In this case a struct list_node is larger than a pointer, so it's just wasting memory and not causing any other harm, but it should be fixed.
Somehow it seems that table->entries[i] is actually not NULL but rather has a struct list_node * type, causing the if condition to fire inappropriately. Could somebody please explain to me why this is?
You dynamically allocate space for table->entries. The initial contents of that allocated space are unspecified, so until you assign values to its contents, it is unsafe to have any particular expectations about them. In particular, you cannot assume that any or all elements will contain null pointers.
If you want to rely on those values to tell you something about what kind of cleanup needs to be performed, then you should set them all to appropriate values, I guess NULL, immediately after allocating the space.
Note also that there are null pointer values of every pointer type, so being null and having type struct list_node * are not mutually exclusive.
I am struggling with the free of memory working with dynamic arrays. Considering the following code:
struct element{
float a;
float b;
};
struct list{
int size;
struct element *myelements;
};
int main(){
struct list mylist;
mylist.size = 0;
mylist.myelements = (struct element*) malloc(sizeof(struct element)*4); //I reserve it as if i had struct element myelements[4]
//i do stuff like
int i;
for(i = 0; i< 4 ; i++){
mylist.myelements[i].a = i;
mylist.myelements[i].b = i*2;
}
//I try to free myelements[2] for example, but i get an error
free(mylist.mylements[3]);
return 0;
}
My question is, how am i supposed to free the second possition of my array of elements. I have thought some alternatives involving realloc:
mylist.mybooks = realloc(mylist.mybooks, sizeof(mylist.mybooks) - sizeof(struct element));
but in that case wouldn´t I have to reorder the elements of the array?
Thanks in advance!
My question is, how am i supposed to free the second possition of my array of elements.
free deallocates the entire allocation done by malloc, it cannot deallocate a part of one allocation.
A common way to manage a resizeable array is to maintain its capacity and size, and when removing elements move subsequent array elements to fill the removed element gap and reduce the size. The spare capacity is used for new elements when they get inserted. Calling realloc for every element insertion/removal is sub-optimal in terms of speed.
I have two structs
struct obj_t {
int id;
float x;
float y;
};
struct cluster_t {
int size;
int capacity;
struct obj_t *obj;
};
As you can see, there is pointer to first obj_t inside cluster_t
What I want to do is to free every obj_t from array inside cluster_t
Do I have to write it with for loop like this?
void clear_cluster(struct cluster_t *c)
{
for(int i = 0; i<c->size;i++)
{
free(&c->obj[i]);
}
free(c->obj);
}
Or is it ok to free the memory just like this ?
void clear_cluster(struct cluster_t *c)
{
free(c->obj);
}
There should be one free() for every malloc() you have, and executed in the opposite order from which it was allocated.
The field obj of cluster_t is a pointer to an array of object_t. This is probably allocated with one malloc() when initializing your cluster_t (something like c->obj = malloc(c->capacity*sizeof(*c->obj))), so it only needs to be freed with one call to free(). You would then want to free the cluster_t allocation itself (assuming it too was dynamically allocated):
free(c->obj);
free(c);
There would be a difference, however, if each object_t itself had a dynamic allocation within it. (In your example, object_t does not.) In that case, you would have needed to iterate through the array and malloc() an allocation when you created the array, and therefore do the reverse and free() each at the end.
It depends on how you allocated. It seems you did something like
struct cluster_t cluster;
cluster.obj = malloc(sizeof (struct obj_t) * SOMENUMBER);
in this case, cluster.obj is just a pointer to an array. All you need to do is
free(cluster.obj)
or
free(c->obj)
in that function which receives a pointer to c.
You only have to iterate over the array calling free if you have an array of pointers.
Remember that & takes the memory address of the variable. You don't free the pointer, you free the memory that the pointer points to. You will never do something like free(&pointer).
Good day!
I need to use malloc in creating a student list system.... In order to be efficient, our professor asked us to use it on a struct so i created a struct as follows:
struct student {
char studentID[6];
char name[31];
char course [6];
};
struct student *array[30];
everytime i add a record, that is when i use malloc...
array[recordCtr]=(struct student*)malloc(sizeof(struct student));
recordCtr++;
then i free it like this.
for(i = 0; i < recordCtr; i++){
free(array[i]);
}
Am i using malloc properly??? what is the effect if i free it like this instead of the loop above.
free(array);
Thanks in advance. Your opinion will be highly appreciated.
You are doing fine.
free(array); would be undefined behavior because array itself was not allocated via malloc therefore you can't free it and don't need to - the memory will be managed by the compiler.
A good tip is to always do:
type *something;
something = malloc(n * sizeof(*something));
This is because, if you change the type of something, you don't have to change all sorts of other code. And sizeof is really a compiler operation here, it won't turn into anything different at runtime.
Also, don't cast the void* pointer returned by malloc, there's no reason to do so in C and it just further ties your code together.
So in your case, don't do:
(struct student*)malloc(sizeof(struct student));
but
malloc(sizeof(**array));
There is nothing illegal about the way you are using malloc but this isn't a list, it's an array of pointers.
To use a list you do not fix the size in advance and have a pointer to the next element. You can either make this intrusive of non-intrusive.
For an intrusive list you put struct student * next in the declaration of student.
For a non-intrusive list you create another struct student_list_node which contains an instance of struct student and a pointer struct student_list_node * next;
This is an exacmple of the non-intrusive version:
struct student_list_node
{
struct student data;
struct student_list_node * next;
};
struct student_list_node * head;
struct student_list_node * tail;
struct student_list_node * addStudentToTail()
{
struct student_list_node * newnode = (struct student_list_node *)(malloc( sizeof(struct student_list_node ) );
/* check malloc did not fail or use a checking vesrion of malloc */
if( !tail )
{
head = tail = newnode;
}
else
{
tail->next = newnode;
tail = newnode;
}
return newnode; // which is also "tail"
}
int main()
{
struct student_list_node * node = addStudentToTail();
struct student * pstud = &node->data;
/* write to pstud student details */
}
If you do really want to use an array, you might want to make it an array of student rather than student * in which case you can use calloc rather than malloc
struct student * array = (struct student *)calloc( 30, sizeof( student ) );
Then using free(array) would be the correct way to dispose of it. You also have the option of allocating more if you need it later with realloc. (Be careful with this one: you must keep a copy of your original pointer until you know realloc succeeds).
The array itself isn't allocated on the heap. Assuming it's a global variable, it is allocated in global memory at program startup and doesn't need to be freed. Calling free on it will probably corrupt your program.
Your current solution is correct.
What you're doing is correct.
You can think of *array[30] as an array of 30 pointers
When you are allocating memory for each of those pointers, you'd also need to call free() on each of them.
Yes, you are using it correctly. There are better ways to organize the storage than this, but this will work. At least until you need more than 30 students...
Note that you must call free() with each pointer that is returned by malloc(). That means that your loop over the array of pointers is the correct approach for your chosen architecture.
Your attempt to call free on the array itself will not work. It invokes Undefined Behavior because you are passing a pointer (to the base of the array itself) to free() that did not come from a call to malloc().
Looks fine.
You could (if it fits your problem) allocate space for all 30 structs in one go
struct student *array = (struct student *)malloc(30*sizeof(struct student));
whhen you want to dispose of the space, you can then do
free(array)
What you have will work just fine. As others have mentioned, you've created an array of pointers on the stack and need to malloc and free each of them individually as you are doing.
However, you don't have to use malloc and free one struct at a time, you can do something like this:
int arraySize = 30;
student * ourArray = (student*)malloc(sizeof(student) * arraySize);
and a single free on the pointer will take care of it. with this pointer, you can still use the bracket notation and the compiler will understand that it's a typed pointer and behave appropriately, giving you essentially the same thing. Which method you use depends on if you need your array a dynamic size or not as well as personal preference.
Hope that helps some.
Initialize your array of pointer to struct student with NULL values
for(i = 0; i < recordCtr; i++){
array[i] = NULL;
}
Free memory if array[i] is not NULL
for(i = 0; i < recordCtr; i++){
if(NULL != array[i])
{
free(array[i]);
}
}
There's simple rule: Every malloc() should be paired with free() with pointer returned by malloc. Not less, not more.
I'm not sure exactly what I need to use as an argument to malloc to allocate space in the table_allocate(int) function. I was thinking just count_table* cTable = malloc(sizeof(count_table*)), but that doesn't do anything with the size parameter. Am I supposed to allocate space for the list_node_t also? Below is what I am working with.
In the .h file I'm given this signature:
//create a count table struct and allocate space for it
//return it as a pointer
count_table_t* table_allocate(int);
Here are the structs that I'm supposed to use:
typedef struct list_node list_node_t;
struct list_node {
char *key;
int value;
//the next node in the list
list_node_t *next;
};
typedef struct count_table count_table_t;
struct count_table {
int size;
//an array of list_node pointers
list_node_t **list_array;
};
count_table* cTable = malloc(sizeof(count_table*))
is wrong. It should be
count_table* cTable = malloc(sizeof(count_table));
Also, you must allocate memory for list_node_t also seperately.
EDIT:
Apart from what Clifford has pointed about allocating memory for the list node, I think the memory allocation should also be taken care for the char *key inside of the list node.
Your suggestion: count_table* cTable = malloc(sizeof(count_table*)) would only allocate space for a pointer to a count_table.
You'd need
count_table* cTable = malloc(sizeof(count_table) ) ;
Each list node would be separately allocated and cTable->size and cTable->list_array and the last list_node_t::next updated accordingly. Maintaining a pointer to the last node added would make adding nodes faster.
I am not sure why count_table::list_array is of type list_node_t** rather than just list_node_t* (and equally called list_array rather than just list). Is it your intention that it is both an array and a list at the same time? That would be somewhat redundant. The member need only be a pointer to the first node, successive nodes are then accessed via list_node::next
Given that the int is a "size" parameter for the created count_table_t, it appears that you are supposed to both allocate the count_table_t itself, as well as initialise its members.
Initialising the list_array member also involves a memory allocation, so it would look like:
count_table_t *table_allocate(int size)
{
count_table_t *table = malloc(sizeof *table);
int i;
table->size = size;
table->list_array = malloc(size * sizeof table->list_array[0]);
for (i = 0; i < size; i++)
table->list_array[i] = NULL;
return table;
}
However, you also need to check for some error conditions: the multiplication of size by sizeof table->list_array[0] could overflow, and either of the malloc() calls could fail. So the function should actually look like this:
count_table_t *table_allocate(int size)
{
count_table_t *table;
int i;
/* Check for overflow in list allocation size */
if (size < 0 || size > (size_t)-1 / sizeof table->list_array[0])
return NULL;
table = malloc(sizeof *table);
if (table == NULL)
return NULL;
table->size = size;
table->list_array = malloc(size * sizeof table->list_array[0]);
if (table->list_array == NULL) {
free(table);
return NULL;
}
for (i = 0; i < size; i++)
table->list_array[i] = NULL;
return table;
}
(Note that (size_t)-1 is a constant equal to the maximum value of a size_t, which is the type of the parameter to malloc()).
In addition to the other posters who point out that you're only allocating enough space for the pointer, not the space the data you want will occupy, I strongly urge you to do things like this:
count_table* cTable = malloc(sizeof(*cTable));
This will help you in case the type of cTable ever changes, you won't have to adjust two parts to that line, just the type.