← All Tools

Ring Buffer Visualizer

A circular buffer stores a fixed number of items in a wrap-around array using two indices: head (next read) and tail (next write). Push wraps at capacity, and full/empty are told apart by tracking count (or a bit).

Size 0 Capacity 8 Head 0 Tail 0 Free 8 State empty

Linear array view

Same storage, laid flat. H = head (next pop), T = tail (next push).

Event log

Canonical C implementation

typedef struct {
    int   *buf;
    size_t cap, head, tail, size;
} rb_t;

bool rb_push(rb_t *r, int v) {
    if (r->size == r->cap) return false;  /* or overwrite: rb_pop() */
    r->buf[r->tail] = v;
    r->tail = (r->tail + 1) % r->cap;
    r->size++;
    return true;
}

bool rb_pop(rb_t *r, int *out) {
    if (r->size == 0) return false;
    *out = r->buf[r->head];
    r->head = (r->head + 1) % r->cap;
    r->size--;
    return true;
}

When you'd use one

Fixed-capacity FIFOs show up in producer/consumer pipelines, embedded serial I/O, ring loggers ("last N events"), lock-free SPSC queues, and audio/DSP block processing. The wrap-around trick means no data copying on pop and no heap allocations at runtime.

The classic gotcha: distinguishing full from empty when head == tail. Fixes: keep a separate size, keep one slot unused (waste 1 of N), or use unmasked read/write indices modulo 2·cap.