1

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?

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
chepukha
  • 2,371
  • 3
  • 28
  • 40
  • Your code looks fine (and works fine on my computer). I'd just remove the casts and apply sizeof to objects rather than types. – pmg Sep 15 '10 at 08:07
  • "exception" ... hmmm: are you compiling your code as `C` (not `C++`)? – pmg Sep 15 '10 at 08:09
  • Your code (with a few irrelevant changes) works ok on ideone's compiler ( http://ideone.com/1QLey ) – pmg Sep 15 '10 at 08:35
  • http://stackoverflow.com/questions/3711233/is-the-struct-hack-technically-undefined-behavior - Though its not a duplicate but definitely worth a look. – Praveen S Sep 15 '10 at 09:31
  • 1
    The code seems OK, but the typecasts look a bit suspect. They should not be needed in C code. Especially, if the compiler complains about the types in your `struct rb *newRB = create(100);` call, then you have some other problem that gets hidden by the typecast. – Bart van Ingen Schenau Sep 15 '10 at 12:47

3 Answers3

3

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
  • 31,821
  • 4
  • 39
  • 33
  • correct. you can't assume how the compiler aligns data in your struct. – vulkanino Sep 15 '10 at 07:46
  • 1
    That's the "struct hack" (flexibla array member in `C99`, which chepukha is using), Jim. It looks ok. – pmg Sep 15 '10 at 07:50
  • +1. But why `calloc(1,sizeof(...))` instead of just `malloc(sizeof(...))` ? – Bart Sep 15 '10 at 07:50
  • 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 Sep 15 '10 at 07:53
  • 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 Sep 15 '10 at 07:56
  • 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 Sep 15 '10 at 07:57
  • Thanks a lot, Jim. I'll try to use it. – chepukha Sep 15 '10 at 08:06
  • 1
    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 Sep 15 '10 at 10:24
  • `newRb->rbArray` is an array type, you can't assign to that, no? – Jens Gustedt Sep 15 '10 at 12:02
  • You are also missing a `*` in the declaration of `rb` and you assing `n` to `count` instead of `size`. – Jens Gustedt Sep 15 '10 at 12:08
  • @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.. GitHub STOP HELPING ICE Sep 15 '10 at 12:58
  • By the way, -1 for recommending against flexible array members, one of the most useful features of C99. – R.. GitHub STOP HELPING ICE Sep 15 '10 at 12:59
  • @R: Sorry, who's recommending against the flexible array hack? – JeremyP Sep 15 '10 at 13:50
  • 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 Sep 16 '10 at 02:45
0

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
  • 76,821
  • 6
  • 102
  • 177
  • `calloc(nmembers, size)` -- "4200 members of size 1 byte" is a little different than "1 member of size 4200 bytes". – pmg Sep 15 '10 at 12:16
  • 1
    @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.. GitHub STOP HELPING ICE Sep 15 '10 at 13:00
0

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
  • 2,371
  • 3
  • 28
  • 40
  • But what is the best answer? Now I don't know what the char** solution looks like. Is it better, worse or as good as the calloc(n, sizeof(char*)) solution? – Elise van Looij Apr 28 '11 at 13:47