#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <glib.h>
void transform_pointer_contents(GArray* const arr) {
GArray* const tmp = g_array_new(true, true, sizeof(uint8_t));
const uint8_t example_num = 5;
g_array_append_val(tmp, example_num);
*arr = *tmp
// Why does this cause a double-free error?
g_array_unref(tmp);
}
int main() {
GArray* const arr = g_array_new(true, true, sizeof(uint8_t));
transform_pointer_contents(arr);
printf("Arr length is %d/n", arr->len);
g_array_unref(arr);
}
Questions
- Why does the unref at the end of
transform_pointer_contents
cause a double-free? - I suspect that the dereferencing of
tmp
also unrefs it somehow, if that is true, how does it work? I think that a similar reference-counted type in C would work by overwriting the dereference operator, but surely C doesn't work that way? - Do I have a guarantee that
tmp
is properly freed without the unref?
CodePudding user response:
- Why does the unref at the end of transform_pointer_contents cause a double-free?
Presumably, it is not either of the GArray
objects themselves that is doubly freed, but rather the dynamic space for the temporary object's data (tmp->data
). That would be because
*arr = *tmp
performs a shallow copy. When you then decrement the reference count of the temporary, GLib deallocates that internal storage, which arr->data
still points to. When arr
later becomes unreferenced, GLib tries to deallocate that data again.
Note that it follows also that the data storage originally allocated for the elements of arr
was leaked.
The bottom line is that GLib does not support copying GArray
s via structure assignment. Instead, use GLib array functions to manipulate GArray
s.
- I suspect that the dereferencing of tmp also unrefs [arr] somehow, if that is true, how does it work?
unrefing tmp
does not unref arr
. But given your unsupported manipulation of arr
, unrefing tmp
has a side effect of deallocting the element storage (then) associated with arr
.
I think that a similar reference-counted type in C would work by overwriting the dereference operator, but surely C doesn't work that way?
The dereference operator (*
) has nothing directly to do with the observed misbehavior, and absolutely nothing to do with GLib's reference counting. Do not conflate the distinct uses of "reference" in play here. And C does not in any case have operator overloading.
The problem is that you have improperly caused the internal state of one GArray
to be aliased to the internal state of another, so that when GLib frees one in response to its reference count dropping to zero, the internal state of the other is corrupted. Not that the aliasing was ever a clean situation in the first place.
- Do I have a guarantee that tmp is properly freed without the unref?
No, you can pretty safely assume that tmp
is not properly freed if its reference count is not reduced to zero. So do continue to unref
it before you lose the pointer to it. What you need to change is the structure assignment, as described above. Use GLib functions to manipulate the GArray
s. There may be viable shortcuts for people who understand GLib well, but at this point, that's not you.