glib

g_slist_last does not retrieve last record in list


After adding 10 structures to a GSList, using g_slist_last to retrieve the last structure does not produce the same result as using g_slist_foreach to view the last structure.

Code sample:

#include <glib-2.0/glib.h>
/* Compile command
gcc main.c -lglib-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
*/

typedef struct {
    int a;
    int b;
} MyStruct;

void print_data(gpointer data, gpointer user_data) {
    MyStruct *my_struct = (MyStruct *)data;
    g_print("a: %d, b: %d\n",my_struct->a, my_struct->b);
}

int main(int argc, char *argv[]) {
    GSList *list = NULL;
    /* Instantiate 10 structures and add them to the GSList */
    for (int i=0; i<10; i++) {
        MyStruct *my_struct = (MyStruct *)g_malloc(sizeof(MyStruct));
        my_struct->a = i;
        my_struct->b = i;
        list = g_slist_append ( list, my_struct);
    }
    /* Get the last structure in the list; print the values */
    MyStruct *my_struct = (MyStruct *)g_slist_last (list);
    g_print("a: %d, b: %d\n",my_struct->a, my_struct->b);

    /* Iterate over each structure in the list; print the values */
    g_slist_foreach (list, print_data, NULL);
    return 0;
}

Output:

a: -554252672, b: 21853 <- result of g_slist_last, which is garbage
a: 0, b: 0
a: 1, b: 1
a: 2, b: 2
a: 3, b: 3
a: 4, b: 4
a: 5, b: 5
a: 6, b: 6
a: 7, b: 7
a: 8, b: 8
a: 9, b: 9 <- last structure in the list which is correct

In this sample, why does g_slist_last give a result different from the last structure using g_slist_foreach?


Solution

  • A GSList structure contains two elements: the next pointer and the data pointer. The latter points to the elements you appended to the list.

    The g_slist_last() returns a pointer to the last GSList structure, not the element you appended. Therefore the cast to MyStuct * is wrong.

    Correct code:

    GSList *last = g_slist_last(list);
    MyStruct *last_data = last->data;
    g_print("a: %d, b: %d\n", last_data->a, last_data->b);
    

    With that change, the output becomes:

    a: 9, b: 9
    a: 0, b: 0
    a: 1, b: 1
    ...
    a: 8, b: 8
    a: 9, b: 9
    

    P.S. To understand this, it might be helpful to use a debugger (or change the code) to print the values of list, my_struct, last and last_data:

    0: list=0x1be2470 my_struct=0x1be2450
    1: list=0x1be2470 my_struct=0x1be2790
    ...
    8: list=0x1be2470 my_struct=0x1be2ec0
    9: list=0x1be2470 my_struct=0x1be2f00
    
       list=0x1be2470 last=0x1be2ee0 last_data=0x1be2f00
    

    Note that on the first iteration list != my_struct even though the list only contains one element, and the last is not the same as last_data.