I am implementing a circular buffer but I am getting a confusing segmentation fault when I am testing my implementation. The code seems to produce a segfault right after I call a function to print out the contents of the circular buffer structure. This confuses me since I would have expected a segfault I am writing or reading from the buffer, but instead it seems to give the error right after returning from my print_cbuf() function.
Here is my circular buffer implementation:
#ifndef __CBUF_H__
#define __CBUF_H__
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <memory.h>
typedef struct
{
char* buffer; // buffer start
char* write; // write pointer in buffer
char* read; // read pointer in buffer
size_t size; // element size
size_t curr_len; // current lenth of circular buffer
size_t max_len; // maximum capacity of circular buffer
} cbuf_t;
/*
* new_circular_buffer()
* Initialize and returns a new circular buffer instance.
*/
cbuf_t* new_circular_buffer(size_t capacity, size_t size)
{
cbuf_t* cbuf = (cbuf_t*) malloc(sizeof(cbuf_t));
if (NULL == cbuf)
return NULL;
char* buffer = (char*) malloc(capacity * size);
if (NULL == buffer)
{
free(cbuf);
return NULL;
}
cbuf->buffer = buffer;
cbuf->write = cbuf->buffer;
cbuf->read = cbuf->buffer;
cbuf->size = cbuf->size;
cbuf->curr_len = 0;
cbuf->max_len = capacity;
return cbuf;
}
/*
* circular_buffer_free()
* Frees all memory the circular buffer has dynamically allocated.
*/
void circular_buffer_free(cbuf_t* cbuf)
{
free(cbuf->buffer);
free(cbuf);
}
/*
* empty()
* Returns True if the circular buffer is empty, False otherwise
*/
bool cbuf_empty(cbuf_t* cbuf)
{
return (bool) (0 == cbuf->curr_len);
}
/*
* full()
* Returns True if the circular buffer is full, False otherwise
*/
bool cbuf_full(cbuf_t* cbuf)
{
return (bool) (cbuf->curr_len == cbuf->max_len);
}
/*
* push()
* Pushes new data into the circular buffer. This will not overwrite data if buffer is full.
*/
bool cbuf_push(cbuf_t* cbuf, void* item)
{
if (cbuf_full(cbuf))
{
return false;
}
memcpy(cbuf->write, item, cbuf->size);
cbuf->write = cbuf->write + cbuf->size;
if (cbuf->write == cbuf->buffer + (cbuf->max_len - 1) * sizeof(item))
{
cbuf->write = cbuf->buffer;
}
cbuf->curr_len++;
return true;
}
/*
* pop()
* Pop data at read pointer from the circular buffer and increments read pointer to next position
*/
void* cbuf_pop(cbuf_t* cbuf)
{
if (cbuf_empty(cbuf))
{
return NULL;
}
void* item;
memcpy(item, cbuf->read, cbuf->size);
cbuf->read = cbuf->read + cbuf->size;
if (cbuf->read == cbuf->buffer + (cbuf->max_len - 1) * sizeof(item))
{
cbuf->read = cbuf->buffer;
}
cbuf->curr_len--;
return item;
}
/*
* size()
* Returns the current size of the circular buffer
*/
size_t cbuf_size(cbuf_t* cbuf)
{
return cbuf->curr_len;
}
/*
* peek()
* Peeks the data at the read pointer without incrementing the read pointer.
*/
void* cbuf_peek(cbuf_t* cbuf)
{
return cbuf->read;
}
/*
* reset()
* Resets all fields in the circular buffer instance and clears the buffer.
*/
void cbuf_reset(cbuf_t* cbuf)
{
memset(cbuf->buffer, 0, cbuf->max_len * cbuf->size);
cbuf->write = cbuf->buffer;
cbuf->read = cbuf->buffer;
cbuf->curr_len = 0;
}
#endif
And here is where I test out the implementation:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <memory.h>
#include "cbuf.h"
void test_circular_buffer(void);
void print_cbuf(cbuf_t* cbuf);
void test_circular_buffer(void)
{
printf("Starting circular buffer API tests...\n");
cbuf_t* cbuf = new_circular_buffer(64, sizeof(char));
if (cbuf == NULL)
{
printf("Could not create circular buffer.\n");
return;
}
printf("=== Circular buffer created. ===\n");
print_cbuf(cbuf); // SEG FAULT HERE ??
// CODE AFTER THIS POINT DOES NOT EXECUTE ?
// Empty Test
printf("cbuf size: %d" , (int) cbuf->curr_len);
if (cbuf_empty(cbuf) == true) {
printf("Empty Test passed!");
} else {
printf("Empty Test failed!");
return;
}
// Push 1 element
char* data;
*data = 13;
assert(cbuf_push(cbuf, data));
printf("=== Data pushed into circular buffer. ===\n");
printf("SUCCESS! All test cases passed!\n");
return;
}
void print_cbuf(cbuf_t* cbuf)
{
printf("Circular Buffer:\n");
printf("Buffer: ");
for (size_t i = 0; i < cbuf->max_len; i++)
{
printf("%d, " , *(char *)(cbuf->buffer+i));
}
printf("\n");
printf("Buffer Start Address: %p\n" , cbuf->buffer);
printf("Buffer Write Address: %p\n", cbuf->write);
printf("Buffer Read Address: %p\n", cbuf->read);
printf("Buffer Current Size: %d/%d\n", (int) cbuf->curr_len, (int) cbuf->max_len);
return;
}
int main(void)
{
test_circular_buffer();
return 0;
}
Here is the error I receive:
Starting circular buffer API tests...
=== Circular buffer created. ===
Circular Buffer:
Buffer: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Buffer Start Address: 0x149606750
Buffer Write Address: 0x149606750
Buffer Read Address: 0x149606750
Buffer Current Size: 0/64
zsh: segmentation fault ./test_cbuf
If someone could shed some light on what is going on here as I am utterly confused and also give feedback on my circular buffer implementation that would be much appreciated. Thanks.