Search code examples
c++stackglib

prev pointer not working for my stack using GList


I'm implementing a stack using GList (doubly) but when I assign my stack with the last element using g_list_last(*stack*) the program doesn't print my stack at all

Pointing to the first element using g_list_first(*stack*) works and I can traverse with stack->next pointer

Here's my test program:

#include <iostream>
#include <cstdlib>
#include <glib.h>

using namespace std;

int main()
{
        cout << "Enter the no of random data to push: ";
        int number = 0;
        cin >> number;

        GList *stack = nullptr;
        for (int i = 0; i < number; i++) {
                int data = random() % 10;
                stack = g_list_append(stack, GINT_TO_POINTER(data));
                cout << "Push: " << data << endl;
        }

        cout << "Printing the stack forward:\n";
        stack = g_list_first(stack);
        while (stack != nullptr) {
                cout << GPOINTER_TO_INT(stack->data);
                cout << "->";
                stack = stack->next;
        }
        cout << "nullptr" << endl;

        cout << "Printing the stack backward:\n";
        stack = g_list_last(stack);
        while (stack != NULL) {
                cout << GPOINTER_TO_INT(stack->data);
                cout << "->";
                stack = stack->prev;
        }
        cout << "nullptr" << endl;

        return 0;
}

Do I have to manually assign the prev link while appending?


Solution

  • First of all, I would not recommend using GLib in a C++ code base; GLib is a C library, full of idiomatic C code and functionality. I'd suggest using the C++ standard library, instead.

    GList is a doubly linked list where each element is composed by three pointers:

    typedef struct _GList GList;
    struct _GList
    {
      void *data; // untyped pointer data
      GList *prev; // pointer to the previous element in the list
      GList *next; // pointer to the next element in the list
    }
    

    For convenience, all the GList functions accept a NULL as a valid list; in the case of g_list_append(), passing a NULL list as the first argument means that it will allocate a new GList element for the data you're passing and place it at the start of the list.

    In your code you're taking the head of the list after populating it, and calling g_list_first(), which is a no-op on the head of the list; then you proceed to consume it by iterating over it, until you hit the end of the list, where you assign nullptr to the stack variable. Since nullptr/NULL is a valid empty GList, you're now calling g_list_last() on a valid, but empty list, which will return NULL, and thus prevent you from iterating backwards. Additionally, you're now leaking the memory allocated to the list.

    The solution is to never iterate a GList with the same variable that holds the head of the list:

            cout << "Printing the stack forward:\n";
            GList *iter = g_list_first(stack);
            while (iter != nullptr) {
                    cout << GPOINTER_TO_INT(iter->data);
                    cout << "->";
                    iter = iter->next;
            }
            cout << "nullptr" << endl;
    

    The code above will consume the iter variable, instead of the stack. Which means that the code below:

            cout << "Printing the stack backward:\n";
            iter = g_list_last(stack);
            while (iter != NULL) {
                    cout << GPOINTER_TO_INT(iter->data);
                    cout << "->";
                    iter = iter->prev;
            }
            cout << "nullptr" << endl;
    

    will work appropriately, and walk the stack backwards, as the stack variable still points to the head of the list, and you're now consuming a temporary iterator.

    Remember to call g_list_free() on the list to release any resources allocated for it—and g_list_free_full() in case you're allocating the contents of the data pointer as well.