11

In the hope of gaining a better understanding of the answers given in this post, can someone please explain to me if the following circular buffer implementation is possible, and if not, why not.

#define CB_TYPE_CHAR     0
#define CB_TYPE_FLOAT    1

...

typedef struct CBUFF
{
    uint16 total;       /* Total number of array elements */
    uint16 size;        /* Size of each array element */
    uint16 type;        /* Array element type */
    uint16 used;        /* Number of array elements in use */
    uint16 start;       /* Array index of first unread element */
    void *elements;     /* Pointer to array of elements */
} CBUFF;

...

void cbRead(CBUFF *buffer, void *element)
{
    if (buffer->type == CB_TYPE_CHAR)
    {
    /* The RHS of this statement is the problem */
        *(char*)element = *(buffer->elements[buffer->start]);
    }

    /* Other cases will go here */

    buffer->start = (buffer->start + 1) % buffer->total;

    --buffer->used;
}

I understand that the LHS must be cast to char so that I can dereference the void pointer. I also understand that this code fragment:

buffer->elements[buffer->start]

gives the address of the 'buffer->start' element of the elements array, which I also want to dereference in order to get to the content of that address. Or at least that's what I take from K&R.

Given all that, how do I tell the compiler that the content of the memory at that address is a char, and that it is okay to dereference it? There is something going on here I just don't understand.

Community
  • 1
  • 1
Zack
  • 463
  • 2
  • 7
  • 15
  • This code does not compile, does it? The `*(buffer->elements[buffer->start])` is a dereference of a `void*`. – Sergey Kalinichenko Dec 04 '12 at 01:34
  • @dasblinkenlight: That's what his question is in regards to. I think he pulled the left hand side from K&R without fully understanding what it does. – Ed S. Dec 04 '12 at 01:35
  • Zack, is the 'type' of your generic buffer fixed once it is initialized and allocated? If so, you may be well served with a `union` of different pointer types to forego all the pointer casting. – WhozCraig Dec 04 '12 at 03:03
  • Thats a great idea - I have never had to use a union before, and had forgotten all about them. I had better go do some reading - thanks for the suggestion! – Zack Dec 04 '12 at 07:06

2 Answers2

14

buffer->elements is also a void * so you need to cast it before you can do anything with it:

*(char*)element = ((char *)buffer->elements)[buffer->start];
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Thanks Chris. I did not realize that such a construction was possible. Am I right in reading this as telling the compiler that the buffer->elements array contains chars, and to then get the contents of the buffer->start nth entry? – Zack Dec 04 '12 at 03:18
  • @Zack: yes, that is exactly correct. Arrays in C are really treated like a pointers to their first element in most ways, so you can cast it to a different pointer type and get the compiler to treat it as if it was a different array type. – Chris Dodd Dec 04 '12 at 22:30
5

Given all that, how do I tell the compiler that the content of the memory at that address is a char, and that it is okay to dereference it?

Well, you've already done it on the LHS of that line:

*(char*)element = *(buffer->elements[buffer->start]);

To derefence buffer->elements[n] you will need to cast that as well.

*(char*)element = *((char*)buffer->elements)[buffer->start];

Now the question is whether or not that cast is correct. I can't tell you that as you did not post the initialization of buffer->elements.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Thanks Ed. I guess I was confused by all the stuff on the RHS. With regards to the initialization, it is done in a function that creates the buffer dynamically, sets the value of a few members, and then buffer->elements = calloc(total, sizeof(size)); – Zack Dec 04 '12 at 03:01