I create drvm *drv
structure in my function. This structure itself contains fields which contains malloc()-ed
fields (uint32_t *buffer
). The code which do that is similar to that:
...
size_t elm_size = sizeof(model*);
uint32_t length = *(uint32_t*)len;
GArray *models = g_array_sized_new(FALSE, FALSE, elm_size, length);
model *mod;
for (int i = 0; i < length; ++i) {
mod = create_model(...);
g_array_append_val(models, mod);
}
This piece of code doesn't contain errors and is highly tested.
At the start of program I register function free_all()
(by atexit()
) which should clean all resources (especially memory) when exit()
is performed.
Inside this function I'm trying freeing memory of elements of GArray*
(model *
structure) and memory for GArray *
itself:
GArray *models;
g_array_set_clear_func(models, clean_model);
if(!g_array_free(models, FALSE)) { //OK }
The problem is that when clean_model(void *data)
is called inside glib
library I suggest it contains pointer to one model *
element. But the address is wrong, it doesn't seem point to any correct value. Neither GArray*
, nor model*
.
Furthermore GArray *models
in free_all()
function is correct (the same as when I created it) and when I iterate through all GArray *
elements in free_all()
by
for (int i = 0; i < len; ++i) {
mod = g_array_index(models, model*, i); // Here I get correct pointer to model*
clean_model(mod);
}
I get expected values.
Question: What's wrong? How should I free memory of elements of GArray *
if these elements contain malloc()-ed
memory?
Part of header:
struct _info {
uint32_t *buffer;
uint32_t len;
};
typedef struct _info info;
struct _prod {
uint32_t *buffer;
uint32_t len;
};
typedef struct _prod prod;
struct _model {
uint32_t name;
prod product;
info inform;
};
typedef struct _model model;
struct _drvm {
GArray *models;
GArray *strings;
};
typedef struct _drvm drvm;
Basically the problem is that your clean_model
function is passed model**
instead of model*
you were expecting.
Remember that GArray
is meant to store complete structs, not just pointers to structs. In order to do that it needs to copy the whole contents of the struct into the internal data
array and therefore any subsequent pointers to the structs (as passed to clean_model
) are going to be pointing somewhere inside data
(i.e. clean_model((elt_type*)&models->data[index * sizeof(elt_type)])
- where in your case elt_type
is model*
)
To fix the situation couple options come to mind, in order of (subjective) preference:
GPtrArray
instead; given that your elements are dynamically allocated already the memory management / pointer handling / typecasts (or lack thereof) would be less confusingclean_model
argument to model**
GArray
to store model
structs rather than pointers, but only makes sense if you can separate the allocation from populating the model contents, e.g. g_array_new(FALSE, FALSE, sizeof(model))
and fill_model(&g_array_index(models, model, i))
In all cases you should also probably pass TRUE
to g_array_free
since you don't seem to be using the GArray.data
for anything afterwards (not that it would make any sense given that you're freeing all the useful data in it anyway.)