Possible Misuse of `g_array_index`, retrieving wrong values

I’m trying to store a GArray inside of a struct and restore it’s elements, but for some reason (possible misuse) it’s returning gibberish. The code is

#include <glib.h>
#include <gmodule.h>

struct entity {
    char name[256];
};


struct Manager {
    char            name[256];
    GArray          *entities;
};


int main(int argc, char *argv[])
{

    struct Manager *mgr = malloc(sizeof *mgr);
    g_strlcpy(mgr->name, "MANAGER", 256);

    mgr->entities = g_array_new(FALSE, FALSE, sizeof(struct entity));

    struct entity *t1 = malloc(sizeof *t1);
    g_strlcpy(t1->name, "name1", 256);

    struct entity *t2 = malloc(sizeof *t2);
    g_strlcpy(t2->name, "name2", 256);

    g_array_append_val(mgr->entities, t1);
    g_print("Entity '%s' added\n", t1->name);

    g_array_append_val(mgr->entities, t2);
    g_print("Entity '%s' added\n", t2->name);

    g_print("# entities: %d\n", mgr->entities->len);

    struct entity *t;
    for(size_t i=0; i< mgr->entities->len; i++)
    {
        t = &g_array_index((mgr->entities), struct entity, i);
        g_print("Entity %s \n", t->name);
    }

    free(t1);
    free(t2);
    g_array_free(mgr->entities, TRUE);
    free(mgr);
    return EXIT_SUCCESS;
}

And the results are:

$ ./array_test 
Entity 'name1' added
Entity 'name2' added
# entities: 2
Entity ^�U 
Entity  _�U 

What is the problem here?

You’re passing a pointer to g_array_append_val. You have to pass a value or use g_array_append_vals(t1, 1). Or just use a GPtrArray if that’s what you want.

May I pass *t1 instead? As in:

g_array_append_val(mgr->entities, *t1);

And no, I don’t want GPtrArray since I want contiguous memory addresses, for cache performance.

I also can move t1 and t2 to the stack, instead malloc them.

Both of those should work I think.

A pretty common trick is also to first g_array_set_size (a, a->len + 1) and then you get the memory for your element via g_array_index(a, a->len - 1) and now you can initialize the element, e.g. via g_strlcpy() like you’re doing in your example. E.g.: https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gtk/gtksnapshot.c#L118

1 Like

May I pass *t1 instead?

You can, but that won’t give you the cache contiguity you’re looking for in the way that you think it will. The storage location for the struct entity instances has to be inside the GArray for that, rather than coming from malloc(). Passing *t1 will memcpy() the data from your malloc() storage location on the heap, into the storage location in the GArray. The malloc() space will then become redundant, so you might as well not malloc() in the first place.

Timm’s second suggestion will do what you want, as will something along the lines of:

my_array = g_array_new (…);
struct entity t1;
g_strlcpy (t1.name, "name1", sizeof (t1.name));
g_array_append_val (my_array, &t1);
1 Like

Compiler complains about the address operand & in g_array_append_val()

In file included from /usr/include/glib-2.0/glib.h:31,
                 from viewport_stacked.c:1:
viewport_stacked.c: In function ‘main’:
/usr/include/glib-2.0/glib/garray.h:64:59: error: lvalue required as unary ‘&’ operand
   64 | #define g_array_append_val(a,v)   g_array_append_vals (a, &(v), 1)

@pwithnall Since we already have a solution, makes no sense spend you time on these details. I have fixed here and I will open another topic regards the actual status of the code.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.