views:

81

answers:

3

Hi all, I'm trying to implement a ring buffer with the following struct

/*head, tail are indexes of the head and tail of ring buffer
 *count is the number of elements; size is the max size of buffer
 *rbArray is an array to pointer of char used to store strings    
 */
struct rb{
  int head;
  int tail;
  int count;
  int size;
  char *rbArray[];
};

Then I use the following function to create a string buffer:

 struct rb *create(int n){
     /*allocate memory for struct*/
     struct rb *newRb = (struct rb*)malloc(sizeof(struct rb)+ n*sizeof(char *));
     assert(newRb);

     int i;
     for(i=0;i<n;i++)
        newRb->rbArray[i] = NULL;

     /*put head and tail at the beginning of array
     initialize count, set max number of elements*/
     newRb->head = 0;
     newRb->tail = 0;
     newRb->count = 0;
     newRb->size = n;

     return newRb;
   }

I call this function in main:

 struct rb *newRB = (struct rb*)create(100);

However, I have problem right at the step allocating memory for struct. In the debugging mode, I can see the value of head, tail, count, were assigned very strange large numbers but not 0. And program hangs after this very first step without throwing me any exception.

Could someone help me explain this problem please? How can I fix it?

Many thanks,

+2  A: 

I have a hard time reading your code, but from what I gather, you probably want to do something along the lines of:

struct rb *create(int n)
{
    struct rb newRb = calloc(1, sizeof(struct rb));
    newRb->rbArray = calloc(n, sizeof(char*));

    newRb->count = n;

    return newRb;
}

calloc will make sure the contents of the allocated space are set to zero. Also, just allocating an additional n*sizeof(char*) with your first call to malloc seems fishy.

Jim Brissom
correct. you can't assume how the compiler aligns data in your struct.
vulkanino
That's the "struct hack" (flexibla array member in `C99`, which chepukha is using), Jim. It looks ok.
pmg
+1. But why `calloc(1,sizeof(...))` instead of just `malloc(sizeof(...))` ?
Bart
Thanks for your quick response. I tried your code but have problem at this line: newRb->rbArray = (char *)calloc(n, sizeof(char*)); Invalid use of flexible array member
chepukha
Yeah, I don't know how to get around with the flexible array member. Because the buffer needs to store strings and the size of array is given at run time, I guess I have to use flex. array member, right?
chepukha
The calloc part is for initialization brevity, I'd follow a malloc with a memset to zero, but this is excatly what calloc does, so...As for the flexible array member stuff: Yes, can be done, but requires C99. And no, you don't exactly need that, you can always use a char** member. As for how it can be done, there is a similar question at SO already: http://stackoverflow.com/questions/1558025/c-initialize-array-within-structure
Jim Brissom
Thanks a lot, Jim. I'll try to use it.
chepukha
Previous to C99 you could still do the flexible array hack, but you needed to declare the final array struct element as size 1 instead of leaving it out. You also had to change the size calculation in malloc to account for the fact that the first array element was included in the sizeof(struct ...) part.
JeremyP
`newRb->rbArray` is an array type, you can't assign to that, no?
Jens Gustedt
You are also missing a `*` in the declaration of `rb` and you assing `n` to `count` instead of `size`.
Jens Gustedt
@JeremyP: You could just leave the size calculation alone. At most you'll waste a few bytes, which is much less that what you'll waste by using two mallocs and a pointer.
R..
By the way, -1 for recommending against flexible array members, one of the most useful features of C99.
R..
@R: Sorry, who's recommending against the flexible array hack?
JeremyP
Thank you guys for helping. I made it work with char**, which is much easier than working around with flex. array member. Thanks a lot Jim.
chepukha
A: 

The following should be a shorter way to do the same:

struct rb *create(int n)
{
    struct rb * newRb = calloc(sizeof(struct rb) + n*sizeof(char*), 1);
    newRb->size = n;    
    return newRb;
}

This sets all the allocated space to 0 and then sets the size field correctly.

Jens Gustedt
`calloc(nmembers, size)` -- "4200 members of size 1 byte" is a little different than "1 member of size 4200 bytes".
pmg
@pmg: they're the same. The only reason to use `nmembers` and `size` separately is if you want to rely on `calloc` doing the overflow check for your multiply.
R..
A: 

Thank you guys a lot for helping. I made it work with char** and it's definitely much easier than working flexibly array member.

However, I wonder, when you have char **array; you can use array[i] and it will give you a pointer to char. Why if we have char *array; we cannot use array[i] to get a char?

Hope I make myself clear enough here.

Thanks

chepukha
You should accept best answer instead.
Bart