简体   繁体   中英

Implementing Queue using Linked list in C

I'm trying to implement queue using linked lists. Here is my program

#include <stdio.h>
#include <stdlib.h>

struct queue_struct{
    int ele;
    struct queue_struct *next;
};

struct Queue{
    struct queue_struct *front, *rear;
};

int isEmpty(struct Queue *q){
    return (q->front==NULL||q->rear==NULL);
}

void enqueue(struct Queue *q, int x){
    struct queue_struct *temp=(struct queue_struct *)malloc(sizeof(struct queue_struct));
    temp->ele=x;
    temp->next=NULL;
    if(isEmpty(q)){
        q->front=q->rear=temp;
        return;
    }
    q->rear=temp;
    printf("The item %d has been enqueued into the queue\n", x);
}

void dequeue(struct Queue *q){
    if(isEmpty(q)){
        printf("The queue is already empty. No more elements can be removed!\n");
        return;
    }
    struct queue_struct *temp=q->front;
    printf("The item %d has been dequeued from the queue\n", temp->ele);
    q->front=q->front->next;
    if(q->front=NULL)
        q->rear=NULL;
    free(temp);
}

void display(struct Queue *q){
    struct queue_struct *temp=q->front;
    int len;
    printf("The contents of the queue are:\n");
    if(isEmpty(q)){
        printf("Nothing to be shown, the queue is empty.\n");
        return;
    }
    while(temp!=NULL){
        temp=temp->next;
        len++;
    }
    temp=q->front;
    for(int i=1;i<len-1;i++){
        printf("|  %d  |\n", temp->ele);
        printf(" ------ \n");
        temp=temp->next;
    }
    printf("|  %d  |\n", temp->ele);
}

int main()
{
    int choice, element;
    printf("LET'S START WITH AN EMPTY QUEUE\n\n");
    struct Queue *q=(struct Queue *)malloc(sizeof(struct Queue));
    q->front=q->rear=NULL;
    while(1){
        printf("\nMENU\n");
        printf("----\n");
        printf("\t1. Enqueue\n");
        printf("\t2. Dequeue\n");
        printf("\t3. Display queue\n");
        printf("\t4. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);
        switch(choice){
            case 1: printf("Enter the element to be enqueued: ");
                    scanf("%d", &element);
                    enqueue(q, element);
                    break;
            case 2: dequeue(q);
                    break;
            case 3: display(q);
                    break;
            case 4: printf("Program terminated successfully!\n");
                    return 0;
            default: printf("Invalid input");
        }
    }
}

However, when I'm trying to enqueue an element, I'm getting a segmentation fault

Upon debugging I find that my isEmpty() function is the culprit, but I can't seem to find the problem. Considering the nature of the problem, I thought the front OR rear should be NULL for the queue to be empty. Am I wrong in my understanding? Any help is appreciated.

Edit
As suggested by @interjay, I have made changes to the q=NULL part of the main() method. However, my display method is now going awry. I don't understand why.

There were some issues.

In main , doing q = NULL; is a memory leak and will produce a segfault. The correct way to initialize an empty queue is:

q->front = NULL;
q->rear = NULL;

The queue display uses a while loop to find the end of the queue, thus guaranteeing that temp will be NULL in the subsequent for loop (ie segfault). Only the for loop is needed

enqueue does not properly handle appending to a non-empty list.

dequeue has an if statement that uses the assignment operator if (q->front = NULL) instead of the equality operator if (q->front == NULL) . But, even with that fix, it still doesn't handle the operation correctly.

There really isn't much use for isEmpty function with the other functions as they can/should just check front .

Don't cast the return value of malloc . See: Do I cast the result of malloc? Doing the cast can introduce subtle, hard to find bugs.


Here's a refactored version that fixes the issues. I did a bit of cleanup/renaming of some of the struct names to be a bit more descriptive of function.

In the code below, I used cpp conditionals to show the changes (eg):

#if 0
// old code
#else
// new code
#endif

I've annotated the bugs where possible:

#include <stdio.h>
#include <stdlib.h>

typedef struct queue_element QElement;
struct queue_element {
    int ele;
    QElement *next;
};

typedef struct Queue {
    QElement *front, *rear;
} Queue;

int
isEmpty(Queue *q)
{
    return (q->front == NULL || q->rear == NULL);
}

void
enqueue(Queue *q, int x)
{
    QElement *temp = malloc(sizeof(*temp));

    temp->ele = x;
    temp->next = NULL;

// NOTE/BUG: this does _not_ append correctly to a non-empty list
#if 0
    if (isEmpty(q)) {
        q->front = q->rear = temp;
        return;
    }

    q->rear = temp;
#else
    // link old rear node to new node
    if (q->rear != NULL)
        q->rear->next = temp;

    // set new rear node
    q->rear = temp;

    // set the front of the list if it was empty
    if (q->front == NULL)
        q->front = temp;
#endif

    printf("The item %d has been enqueued into the queue\n", x);
}

void
dequeue(Queue *q)
{
    QElement *temp = q->front;

    if (temp == NULL) {
        printf("The queue is already empty. No more elements can be removed!\n");
        return;
    }

    printf("The item %d has been dequeued from the queue\n", temp->ele);

    q->front = temp->next;
    if (q->rear == temp)
        q->rear = NULL;

    free(temp);
}

void
dequeue_OLD(Queue *q)
{
    if (isEmpty(q)) {
        printf("The queue is already empty. No more elements can be removed!\n");
        return;
    }
    QElement *temp = q->front;

    printf("The item %d has been dequeued from the queue\n", temp->ele);
    q->front = q->front->next;
// NOTE/BUG: this if is invalid -- it is using the assignment operator in
// place of the [desired] equality operator
// NOTE/BUG: even with this fix, the dequeue is still broken -- see the fix
// above
#if 0
    if (q->front = NULL)
        q->rear = NULL;
#else
    if (q->front == NULL)
        q->rear = NULL;
#endif
    free(temp);
}

void
display(Queue *q)
{
    QElement *temp = q->front;

    printf("The contents of the queue are:\n");

    if (temp == NULL) {
        printf("Nothing to be shown, the queue is empty.\n");
        return;
    }

// NOTE/BUG: doing the while loop ensures that temp will be NULL so that the
// for loop will try to dereference temp and it will segfault
#if 0
    int len;
    while (temp != NULL) {
        temp = temp->next;
        len++;
    }
    for (int i = 1; i < len - 1; i++) {
        printf("|  %d  |\n", temp->ele);
        printf(" ------ \n");
        temp = temp->next;
    }
    printf("|  %d  |\n", temp->ele);
#else
    int sep = 0;
    for (;  temp != NULL;  temp = temp->next) {
        if (sep)
            printf(" ------ \n");
        sep = 1;
        printf("|  %d  |\n", temp->ele);
    }
#endif
}

int
main(void)
{
    int choice, element;

    printf("LET'S START WITH AN EMPTY QUEUE\n\n");
    Queue *q = malloc(sizeof(*q));
// NOTE/BUG: setting q to NULL ensures a segfault and is a memory leak
#if 0
    q = NULL;
#else
    q->front = NULL;
    q->rear = NULL;
#endif

    while (1) {
        printf("\nMENU\n");
        printf("----\n");
        printf("\t1. Enqueue\n");
        printf("\t2. Dequeue\n");
        printf("\t3. Display queue\n");
        printf("\t4. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);
        switch (choice) {
        case 1:
            printf("Enter the element to be enqueued: ");
            scanf("%d", &element);
            enqueue(q, element);
            break;
        case 2:
            dequeue(q);
            break;
        case 3:
            display(q);
            break;
        case 4:
            printf("Program terminated successfully!\n");
            return 0;
        default:
            printf("Invalid input");
            break;
        }
    }

    return 0;
}

There are the following issues:

  • You should not assign NULL to q , since that is the pointer to the memory you had just allocated, and which holds the pointers front and rear , which you will need always , even when the queue is empty. Instead you should set those front and rear pointers to NULL :

     q->front = q->rear = NULL;
  • In dequeue you have an assignment inside an if condition, where you actually want a comparison . Change this:

     if(q->front=NULL)

    to:

     if(q->front==NULL)
  • In display you are dereferencing temp after the first loop, where you are sure that it is actually NULL . So this is going to raise an exception. You should only have one loop. Any attempt to loop again over the list with the same temp pointer would need the temp pointer to be reset to the front . Here is a simple replacement for your display function:

     void display(struct Queue *q){ struct queue_struct *temp=q->front; printf("The contents of the queue are:\n"); if(isEmpty(q)){ printf("Nothing to be shown, the queue is empty.\n"); return; } while(temp,=NULL){ printf("%d->"; temp->ele); temp=temp->next; } printf("NULL\n"); }

    if you want an i counter to be displayed as well, then just increment it in the same loop above.

With these changes your code will work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM