I have seen the following implementation of circular buffer:
http://en.wikipedia.org/wiki/Circular_buffer
/**< Buffer Size */
#define BUFFER_SIZE 10
#define NUM_OF_ELEMS (BUFFER_SIZE-1)
/**< Circular Buffer Types */
typedef unsigned char INT8U;
typedef INT8U KeyType;
typedef struct
{
INT8U writePointer; /**< write pointer */
INT8U readPointer; /**< read pointer */
INT8U size; /**< size of circular buffer */
KeyType keys[0]; /**< Element of circular buffer */
} CircularBuffer;
/**< Init Circular Buffer */
CircularBuffer* CircularBufferInit(CircularBuffer** pQue, int size)
{
int sz = size*sizeof(KeyType)+sizeof(CircularBuffer);
*pQue = (CircularBuffer*) malloc(sz);
if(*pQue)
{
printf("Init CircularBuffer: keys[%d] (%d)\n", size, sz);
(*pQue)->size=size;
(*pQue)->writePointer = 0;
(*pQue)->readPointer = 0;
}
return *pQue;
}
int main(int argc, char *argv[])
{
CircularBuffer* que;
KeyType a = 101;
int isEmpty, i;
CircularBufferInit(&que, BUFFER_SIZE);
...
}
Here are the questions:
Q1> Why the code uses the following line to define variable keys?
KeyType keys[0]; /**< Element of circular buffer */
Q2> Why the code computes the size of the allocated buffer as follows:
int sz = size*sizeof(KeyType)+sizeof(CircularBuffer);
Q3> Why pQue pointing to a buffer larger than the size of CircularBuffer, but still can
directly referring to the member of it?
(*pQue)->size=size;
(*pQue)->writePointer = 0;
(*pQue)->readPointer = 0;
The first item is just to specify that the element in the struct uses array syntax, but actually isn’t declared to be an array of any size.
The malloc allocates the circular buffer size (which accounts for all the of non-key fields) and the key fields (size * sizeof(KeyType)). Note that keys[0] is actually of size zero, so no key field gets counted twice.
The buffer isn’t actually larger than the size of the circular buffer, the allocation (as described above) was a one-time allocation to accommodate the circular buffer and the control elements (size, readPointer, writePointer) in one pass.
The whole reason this works is because C doesn’t check to see if you walked off the end of an array. In a language that enforces array bounds, the first time you attempted to use this you would get something akin to Java’s ArrayOutOfBoundsException, because to use keys[0], you would have had to declare a keys array of size (at least) one, like keys[1].
In other words, it’s a couple of C specific hacks to optimize the allocation of the buffer once, without encoding a fixed size. The reason it works is because array offset is strictly implemented as (base address + index * sizeof(array type)).