0

I am writing a simple queue in c using a linked list that keeps track of the last element.

This is my code:

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

typedef struct node_s {
    uint32_t x, y;
} node;


// very cheap linked list queue that lives as long as its scope
typedef struct queue_s {
    node* node;
    struct queue_s* next;
} queue;

bool isEmpty(queue** queue)
{
    return !*queue;
}

node* dequeue(queue** head, queue** tail)
{
    if (isEmpty(head)) {
        printf("attempting to dequeue from an empty queue\n");
        return NULL;
    }
    node* ret = (**head).node;
    *head = (*head)->next;
    if (isEmpty(head)) *tail = NULL;
    printf("next %p\n", (void*) *head);
    // no need to free anything, the queue lives as long as its scope
    return ret;
}

void enqueue(queue** head, queue** tail, queue* new)
{
    if (isEmpty(head)) {
        // has no head -> make new head
        *head = new;
        *tail = new;
        printf("empty\n");
    } else {
        // has head -> make new tail // TODO does this work?
        (*tail)->next = new;
        *tail = (*tail)->next;
        printf("not empty\n");
    }
}

int main()
{
    queue* head = NULL;
    queue* tail = NULL;

    enqueue(&head, &tail, &(queue) {.node=&(node) {1, 1}, NULL});
    printf("h %i\n", head->node->x);
    printf("t %i\n", tail->node->x);

    enqueue(&head, &tail, &(queue) {.node=&(node) {2, 2}, NULL});
    printf("h %i\n", head->node->x);
    printf("t %i\n", tail->node->x);

    enqueue(&head, &tail, &(queue) {.node=&(node) {3, 3}, NULL});
    printf("h %i\n", head->node->x);
    printf("t %i\n", tail->node->x);

    // for (int i = 1; i <= 3; ++i) {
    //  enqueue(&head, &tail, &(queue) {.node=&(node) {i, i}, NULL});
    //  printf("h %i\n", head->node->x);
    //  printf("t %i\n", tail->node->x);
    // }

    return 0;
}

Now the rolled out for loop in main() behaves correctly as the head element stays 1 while new elements are queued:

empty
h 1
t 1
not empty
h 1
t 2
not empty
h 1
t 3

...but the for loop itself gives a wrong output as the head element is always equal to the tail element:

empty
h 1
t 1
not empty
h 2
t 2
not empty
h 3
t 3

I'm a c beginner and this is driving me insane, can someone please help me?

Matthias
  • 737
  • 8
  • 14

1 Answers1

2

This code:

for (int i = 1; i <= 3; ++i) {
      enqueue(&head, &tail, &(queue) {.node=&(node) {i, i}, NULL});
      printf("h %i\n", head->node->x);
      printf("t %i\n", tail->node->x);
}

is equivalent to this:

for (int i = 1; i <= 3; ++i) 
      queue next = {.node=&(node) {i, i}, NULL};
      enqueue(&head, &tail, &next);
      printf("h %i\n", head->node->x);
      printf("t %i\n", tail->node->x);
} // End of scope

The new element next will cease to exist at the end of the scope (see comment). What you need to do to get this working in a loop is dynamic memory allocation.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • Thank you, I thought it would be existant until the function returns, which would be exactly what I need. I read a little and, as I understand, that is not possible, as i have to declare the queue "localised to the for loop block" for each queue to get a unique pointer. So I must use malloc, right? – Matthias Aug 11 '18 at 15:48
  • @Matt Yes you would probably need to use malloc. When I think of it, it is a pretty strange idea to implement something like this without malloc, because of all the limitations you will get from not doing so. – klutt Aug 11 '18 at 15:57
  • I would have liked to implement it without malloc because I wouldn't have had to worry about freeing again and could have just let the variable go out of scope. I only need the queue in the scope it is created in, I don't want to return it and use it anywhere else. – Matthias Aug 12 '18 at 12:36