Skip to content

Queue in C

Updated 7 min read Aimerscrew

A stack serves the newest item first, which isn't fair when people are waiting their turn. A queue in C serves the oldest item first. In this lesson, you'll learn how queues work, why a plain array runs into trouble, and how to build a circular queue that reuses its slots by wrapping around with the modulo operator.

🌍 Real-life example

Think of the line at a coffee shop. New customers join at the back, and the barista serves whoever is at the front. Nobody who arrived later gets served first. An office printer does the same with documents: if you, then a coworker, then your manager hit Print, the pages come out in exactly that order.

What is a queue?

A queue is a collection where items are added at one end (the rear) and removed from the other end (the front). This rule is called FIFO: First In, First Out. The item that has waited longest always leaves next.

OperationWhat it does
enqueueAdd an item at the rear
dequeueRemove the item at the front and give it back
is_emptyCheck whether the queue has no items
is_fullCheck whether a fixed-size queue has no room left

Stack vs queue

StackQueue
OrderLIFO: last in, first outFIFO: first in, first out
Add atTopRear
Remove fromTopFront
Everyday exampleStack of plates, Back buttonCheckout line, print jobs

The problem with a simple array queue

Suppose you store a queue in an array of 4 slots. You add at index 0, 1, 2, 3 and remove from index 0, then 1. Now slots 0 and 1 are free, but the rear has reached the end of the array, so you can't add anything else. The queue looks full while half of it is empty.

You could shift every item to the left after each dequeue, but that's slow for big queues. The better idea is to let the indexes wrap around to the start of the array.

Wrap-around with the modulo operator

The modulo operator % gives the remainder of a division. If you move an index forward with index = (index + 1) % CAPACITY, it counts 0, 1, 2, 3 and then jumps back to 0, like the hand of a clock.

#include <stdio.h>

int main(void) {
    int slot = 0;
    for (int step = 1; step <= 6; step++) {
        printf("Step %d: slot %d\n", step, slot);
        slot = (slot + 1) % 4;
    }
    return 0;
}
Step 1: slot 0
Step 2: slot 1
Step 3: slot 2
Step 4: slot 3
Step 5: slot 0
Step 6: slot 1

After slot 3, (3 + 1) % 4 is 0, so the index wraps back to the beginning. A queue built this way is called a circular queue or circular buffer.

How a circular queue works

Our queue keeps two numbers: front, the index of the first item, and count, how many items are stored. The rear position, where the next item goes, is calculated as (front + count) % CAPACITY.

Animation: a circular queue of 4 slots. 101, 102 and 103 are enqueued into slots 0 to 2, 101 is dequeued and front moves to slot 1, 104 goes into slot 3, and 105 wraps around into slot 0, leaving the queue full
When the rear reaches the last slot, the next item wraps around to slot 0, reusing the space freed by dequeue.

The full and empty conditions are simple with a counter:

  • Empty: count == 0
  • Full: count == CAPACITY

Circular queue implementation in C

This program models a coffee shop with room for 4 waiting orders. Like the stack lesson, the queue lives in a struct, and dequeue() sends the removed value back through a pointer.

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

#define CAPACITY 4

struct Queue {
    int items[CAPACITY];
    int front;   /* index of the first item */
    int count;   /* number of items stored */
};

void init_queue(struct Queue *q) {
    q->front = 0;
    q->count = 0;
}

bool is_empty(const struct Queue *q) {
    return q->count == 0;
}

bool is_full(const struct Queue *q) {
    return q->count == CAPACITY;
}

bool enqueue(struct Queue *q, int value) {
    if (is_full(q)) {
        printf("Queue is full: cannot add %d\n", value);
        return false;
    }
    int rear = (q->front + q->count) % CAPACITY;
    q->items[rear] = value;
    q->count++;
    return true;
}

bool dequeue(struct Queue *q, int *out) {
    if (is_empty(q)) {
        printf("Queue is empty: nothing to remove\n");
        return false;
    }
    *out = q->items[q->front];
    q->front = (q->front + 1) % CAPACITY;
    q->count--;
    return true;
}

int main(void) {
    struct Queue line;
    init_queue(&line);
    int order = 0;

    enqueue(&line, 101);
    enqueue(&line, 102);
    enqueue(&line, 103);

    dequeue(&line, &order);
    printf("Served order %d\n", order);

    enqueue(&line, 104);   /* goes into slot 3 */
    enqueue(&line, 105);   /* wraps around into slot 0 */
    enqueue(&line, 106);   /* no room left */

    while (!is_empty(&line)) {
        dequeue(&line, &order);
        printf("Served order %d\n", order);
    }

    dequeue(&line, &order);  /* one too many */
    return 0;
}
Served order 101
Queue is full: cannot add 106
Served order 102
Served order 103
Served order 104
Served order 105
Queue is empty: nothing to remove

Tracing the circular queue step by step

Here's what happens inside the array. _ marks a slot we treat as free.

OperationSlots [0] [1] [2] [3]frontcount
enqueue 101, 102, 103101 102 103 _03
dequeue (101)_ 102 103 _12
enqueue 104: rear = (1 + 2) % 4 = 3_ 102 103 10413
enqueue 105: rear = (1 + 3) % 4 = 0105 102 103 10414 (full)
enqueue 106no change14

Then the loop serves 102 (slot 1), 103 (slot 2), 104 (slot 3), and front wraps to slot 0 to serve 105. The orders come out in exactly the order they arrived.

📝 Note

Another common design stores front and rear indexes instead of a count. Then "empty" is front == rear, and "full" is (rear + 1) % CAPACITY == front. That version always leaves one slot unused so that full and empty don't look the same. The counter version above uses every slot and is easier to read.

💡 Tip

If you don't know the maximum size, build the queue on a linked list with two pointers: head for dequeuing and tail for enqueuing. Both operations stay fast, and the queue grows with malloc(). Just remember to free every node.

Where are queues used?

  • Print spoolers: documents print in the order they were sent.
  • Keyboard buffers: keys you type are processed in order, even if the program is busy for a moment.
  • Streaming audio and video: a circular buffer holds the next few seconds so playback stays smooth.
  • Customer support and ticket systems: requests are handled first come, first served.

Common mistakes with queues

1. Forgetting the modulo

Writing q->front = q->front + 1; without % CAPACITY lets the index run past the end of the array. Reading items[4] in a 4-slot array is undefined behavior and may return garbage or crash.

2. Using front == rear for both full and empty

With only front and rear and no count, a full queue and an empty queue have the same indexes. Either keep a count or leave one slot unused, as described in the note above.

3. Dequeuing without checking for empty

Removing from an empty queue returns an old leftover value, so your program processes an order that doesn't exist. Always check is_empty() or the return value of dequeue().

4. Mixing up front and rear

Items join at the rear and leave from the front. Removing from the rear turns your queue into a stack.

Practice exercise

🏋️ Try it yourself

A printer queue has room for 3 jobs. Using a circular queue with front and count, enqueue job pages 5, 12 and 3, print one job, then enqueue 8 and print all remaining jobs. Print each job as Printing a job of N pages.

Show solution
#include <stdio.h>
#include <stdbool.h>

#define CAPACITY 3

struct Queue {
    int items[CAPACITY];
    int front;
    int count;
};

bool enqueue(struct Queue *q, int value) {
    if (q->count == CAPACITY) {
        return false;
    }
    q->items[(q->front + q->count) % CAPACITY] = value;
    q->count++;
    return true;
}

bool dequeue(struct Queue *q, int *out) {
    if (q->count == 0) {
        return false;
    }
    *out = q->items[q->front];
    q->front = (q->front + 1) % CAPACITY;
    q->count--;
    return true;
}

int main(void) {
    struct Queue printer = { .front = 0, .count = 0 };
    int pages = 0;

    enqueue(&printer, 5);
    enqueue(&printer, 12);
    enqueue(&printer, 3);

    if (dequeue(&printer, &pages)) {
        printf("Printing a job of %d pages\n", pages);
    }

    enqueue(&printer, 8);   /* wraps around into slot 0 */

    while (dequeue(&printer, &pages)) {
        printf("Printing a job of %d pages\n", pages);
    }
    return 0;
}
Printing a job of 5 pages
Printing a job of 12 pages
Printing a job of 3 pages
Printing a job of 8 pages

What's next?

You can now store data in arrays, linked lists, stacks and queues. Next, learn how to put that data in order with sorting algorithms in C.

Key takeaways

  • A queue is a FIFO structure: the first item added is the first item removed.
  • enqueue adds at the rear and dequeue removes from the front.
  • A circular queue reuses array slots by wrapping indexes with index = (index + 1) % CAPACITY.
  • With a count, the queue is empty when count == 0 and full when count == CAPACITY.
  • The rear position can be calculated as (front + count) % CAPACITY.
  • Queues power print spoolers, keyboard buffers, streaming and first-come, first-served systems.

Frequently asked questions

What is a queue in C?

A queue is a data structure that follows First In, First Out (FIFO) order. Items are added at the rear with enqueue and removed from the front with dequeue. C has no built-in queue, so you build one with an array or a linked list.

What is a circular queue?

A circular queue stores items in a fixed array and wraps its indexes back to 0 after the last slot using the modulo operator. This reuses the space freed by dequeue instead of shifting items.

How do you check if a circular queue is full?

If you keep a count of items, the queue is full when count equals the capacity. If you only keep front and rear indexes, it is full when (rear + 1) % CAPACITY equals front, which leaves one slot unused.

Why use modulo in a circular queue?

The modulo operator turns an index that goes past the end of the array back into a valid index. For a 4-slot array, (3 + 1) % 4 is 0, so the next item goes into the first slot.

What is the difference between a stack and a queue in C?

A stack is LIFO: you add and remove at the same end, so the newest item leaves first. A queue is FIFO: you add at the rear and remove from the front, so the oldest item leaves first.