Home | Projects | Notes > Data Structures & Algorithms > Ring Buffer
A ring buffer is a fixed-size data structure that uses a single, contiguous block of memory as a circular queue. It maintains two indices (front and back) to manage reading and writing. When the end of the buffer is reached, it wraps around to the beginning — forming a logical circle.
Constant-Time Operations: O(1) time for enqueue and dequeue (no shifting of elements).
Fixed Memory Footprint: Efficient for systems with limited memory or real-time constraints.
Cache-Friendly: Data is stored contiguously in memory.
Ideal for Streaming: Commonly used in audio buffers, UART RX/TX buffers, and producer-consumer models.
Fixed Capacity: Must predefine buffer size; resizing is non-trivial.
Wasted Slot (optional): Some implementations leave one slot empty to distinguish full vs. empty.
Requires Careful Index Management: Logic for wraparound and full/empty detection can be error-prone.
Not Suited for Arbitrary Insertion/Deletion: Only supports FIFO (queue-style) behavior.
rbuffer.hpp)x123
45
6class rbuffer7{8public:9 rbuffer(const int cap);10 ~rbuffer();11 bool empty() const;12 bool full() const;13 void push(const int val);14 int pop();15 int front() const;16 int size() const;17 int capacity() const;18 void clear();19
20private:21 int *buffer;22 int cap;23 int head;24 int tail;25 int sz;26};27
28rbuffer.cpp)74123
4rbuffer::rbuffer(const int cap)5 : cap(cap), head(0), tail(0), sz(0)6{7 buffer = new int[cap];8}9
10rbuffer::~rbuffer()11{12 delete[] buffer;13}14
15bool rbuffer::empty() const16{17 return sz == 0;18}19
20bool rbuffer::full() const21{22 return sz == cap;23}24
25void rbuffer::push(const int val)26{27 if (full())28 {29 throw std::overflow_error("Ring buffer overflow");30 }31
32 buffer[tail++] = val;33 tail %= cap;34 ++sz;35}36
37int rbuffer::pop()38{39 if (empty())40 {41 throw std::underflow_error("Ring buffer underflow");42 }43
44 int val = buffer[head++];45 head %= cap;46 --sz;47
48 return val;49}50
51int rbuffer::front() const52{53 if (empty())54 {55 throw std::runtime_error("Ring buffer is empty");56 }57
58 return buffer[head];59}60
61int rbuffer::size() const62{63 return sz;64}65
66int rbuffer::capacity() const67{68 return cap;69}70
71void rbuffer::clear()72{73 head = tail = sz = 0;74}915 020 33041 550657180 393
rbuffer.h)42123
45
6typedef struct7{8 int *buffer;9 int capacity;10 int head;11 int tail;12 int size;13} rbuffer;14
15// initializes the ring buffer16bool rbuffer_init(rbuffer *rb, const int cap);17
18// frees allocated memory19void rbuffer_destroy(rbuffer *rb);20
21// adds an element to the buffer22bool rbuffer_push(rbuffer *rb, const int val);23
24// removes the oldest element25bool rbuffer_pop(rbuffer *rb);26
27// finds the oldest element if any28bool rbuffer_front(rbuffer *rb, int *out_val);29
30// checks if buffer is empty31bool rbuffer_empty(const rbuffer *rb);32
33// checks if buffer is full34bool rbuffer_full(const rbuffer *rb);35
36// clears the buffer37void rbuffer_clear(rbuffer *rb);38
39// returns the number of elements currently in the buffer40int rbuffer_size(const rbuffer *rb);41
42rbuffer.c)1271234
5// initializes the ring buffer6bool rbuffer_init(rbuffer *rb, const int cap)7{8 if (!rb || cap <= 0)9 {10 return false;11 }12
13 rb->buffer = (int *)malloc(cap * sizeof(int));14 if (!rb->buffer)15 {16 // memory allocation failed17 return false;18 }19
20 rb->head = 0;21 rb->tail = 0;22 rb->size = 0;23 rb->capacity = cap;24
25 return true;26}27
28// frees allocated memory29void rbuffer_destroy(rbuffer *rb)30{31 if (!rb)32 {33 return;34 }35
36 if (rb->buffer)37 {38 free(rb->buffer);39 rb->buffer = NULL;40 }41
42 rb->head = 0;43 rb->tail = 0;44 rb->size = 0;45 rb->capacity = 0;46}47
48// adds an element to the buffer49bool rbuffer_push(rbuffer *rb, const int val)50{51 if (!rb || !rb->buffer || rbuffer_full(rb))52 {53 return false;54 }55
56 rb->buffer[rb->tail] = val;57 rb->tail = (rb->tail + 1) % rb->capacity;58 rb->size++;59
60 return true;61}62
63// removes the oldest element64bool rbuffer_pop(rbuffer *rb)65{66 if (!rb || !rb->buffer || rbuffer_empty(rb))67 {68 return false;69 }70
71 rb->head = (rb->head + 1) % rb->capacity;72 rb->size--;73
74 return true;75}76
77// finds the oldest element if any78bool rbuffer_front(rbuffer *rb, int *out_val)79{80 if (!rb || !rb->buffer || rbuffer_empty(rb))81 {82 return false;83 }84 85 if (out_val)86 {87 *out_val = rb->buffer[rb->head];88 }89
90 return true;91}92
93// checks if buffer is empty94bool rbuffer_empty(const rbuffer *rb)95{96 return !rb || (rb->size == 0);97}98
99// checks if buffer is full100bool rbuffer_full(const rbuffer *rb)101{102 return !rb && rb->buffer && (rb->size == rb->capacity);103}104
105// clears the buffer106void rbuffer_clear(rbuffer *rb)107{108 if (!rb)109 {110 return;111 }112
113 rb->head = 0;114 rb->tail = 0;115 rb->size = 0;116}117
118// returns the number of elements currently in the buffer119int rbuffer_size(const rbuffer *rb)120{121 if (!rb)122 {123 return 0;124 }125
126 return rb->size;127}32123
4int main(int argc, char *argv[])5{6 rbuffer rb;7
8 rbuffer_init(&rb, 5);9
10 rbuffer_push(&rb, 10);11 rbuffer_push(&rb, 20);12 rbuffer_push(&rb, 30);13 rbuffer_push(&rb, 40);14 rbuffer_push(&rb, 50);15
16 int val;17 while (!rbuffer_empty(&rb))18 {19 if (rbuffer_front(&rb, &val))20 {21 printf("%d\n", val);22 }23 24 rbuffer_pop(&rb);25 }26
27 rbuffer_destroy(&rb);28
29 printf("%d %d\n", rbuffer_empty(&rb), rbuffer_full(&rb));30
31 return 0;32}111022033044055061 0