Home > Net >  C Linked List Queue pointers causing undefined behavior
C Linked List Queue pointers causing undefined behavior

Time:05-09

I've read the questions and not seen answer to mine. Most people use structs for these and after testing reference code I see the struct version is functional but, why is class version not?

#include <iostream>
using namespace std;

// I'm still bad at pointer logic but it makes sense.
// REFERENCES
// https://www.geeksforgeeks.org/queue-linked-list-implementation/
// stack linked list assignment earlier in semester
// https://stackoverflow.com/questions/29787026/c-queue-from-linked-list
class Node {
    public:
    int data;
    Node *next;         // controls flow of nodes
};

class Queue {
    public:
    Queue();
    ~Queue();
    void enQueue(int data);
    void deQueue();
    bool isEmpty();
    
    //private: all set to public because I don't want to make a print function
    Node *front;
    Node *rear;
    int counter;
};

Queue::Queue()

{
    
    front = NULL;
    rear  = NULL;
    
}

Queue::~Queue() {
    
    if (rear == NULL && front == NULL)
    {
        cout << "Nothing to clean up!" << endl;
    }
    else
    {
        cout << "Delete should be happening now...";
    }
}

void Queue::enQueue(int data) {

    // create new node of queue structure
    Node *temp  = new Node;
    temp->data  = data;
    
    // write like this line below if not temp->data = data;
    // Node *temp = new Node(data); 
    
    // if the queue is empty, first node.
    if (rear == NULL)
    {
        front = temp;
        rear  = temp;
        return;
    }
    
    // assign data of queue structure

    rear->next  = temp; // point rear pointer to next pointer = assign to temp
    rear = temp; // assign rear to temp keep front pointer at beginning fifo
}

void Queue::deQueue()
{

    // if queue is empty, return NULL
    if (front == NULL)
    {
        cout << "Queue is empty, sorry!";

        return;
    }
    
    Node*   temp = front;       // assign front 
    
    //problem here
    front = front->next;        // move front one node ahead
    
    if(front == NULL)
    {
     rear = NULL;
    }
     delete (temp);
    
    return;
    }
    
bool Queue::isEmpty() 
{
    //   cout << "functions";
    //   cout << endl;
    return (front == NULL && rear == NULL);
}

int main()
{
    Queue yay;
    yay.isEmpty();
    yay.deQueue();
    cout << endl;
    yay.enQueue(5);
    yay.enQueue(6);


    cout << "Queue Front : " << (yay.front)->data << endl;
    yay.deQueue();
    cout << "After deqQueue " << endl;
    cout << "Queue Front : " << (yay.front)->data << endl;
    yay.deQueue();
    cout << "Queue Front : " << (yay.front)->data << endl;
    yay.deQueue(); // <- Problem here
    yay.deQueue();
    cout << "completed" << endl;
}

I've isolated the problem to line 90

    //problem here
front = front->next;        // move front one node ahead

it causes this code in int main() to not print

    yay.deQueue(); // <- Problem here
yay.deQueue();
cout << "completed" << endl;

I know I am weak at pointer directing and so forth, so if could please tell me what I am not seeing would be appreciated, I've been poking at it for a while now and did not resolve.

This link for code through online gdb

https://onlinegdb.com/0B0KzcHMa

This is the output with line 
Queue is empty, sorry!
Queue Front : 5
After deQueue
Queue Front : 6

And the output with line 90 and 96 commented out //96 commented to avoid double free


Queue is empty, sorry!
Queue Front : 5
After deQueue
Queue Front : 5
Queue Front : 5
completed
Delete should be happening now...


Now I know, I know I should be better at this traversal of nodes business 
but, I am not seeing it and this may help me remember it in the future :X

I believe its a simple fix but, everything I try reaches the former output
rather than the later with intended data represented, I believe it is
nexting out into random memory making memory leak or memory pointing wherever
thus it never goes back to main to complete terminal messages

CodePudding user response:

The problem is here:

    Queue yay;
    yay.isEmpty();
    yay.deQueue();
    cout << endl;
    yay.enQueue(5);
    yay.enQueue(6);


    cout << "Queue Front : " << (yay.front)->data << endl;
    yay.deQueue();
    cout << "After deqQueue " << endl;
    cout << "Queue Front : " << (yay.front)->data << endl;
    yay.deQueue();
    cout << "Queue Front : " << (yay.front)->data << endl; // <<==== HERE

The queue had two elements, 5 and 6, inserted. Then one was popped, then the other. That means yay.front is now nullptr per your deQueue logic reading the end of the list. Therefore, reading (yay.front)->data invoke undefined behavior by dereferencing an invalid pointer (in this case a pointer containing nullptr).

Lose that dereference (one way or another) and the code shouldn't fault any longer.

  • Related