0

I am trying to make a struct for a circular buffer that contains an array of type "quote." However, the quote array must start out at a size of 10. I am trying to figure out if I declare the size of 10 in my .h file or in my .c file. My two files are as follows:

.h file:

typedef struct{
    unsigned int time;
    double rate;

}quote;

typedef struct{

    unsigned int testNum;
    quote quoteBuffer[];

}cbuf;

cbuf* cbuf_init();

.c file:

cbuf* cbuf_init(){

    cbuf *buffer = (cbuf *)calloc(1,sizeof(cbuf));
    buffer->testNum = 1;
    quote newQuote = {1,1.00};
    buffer->quoteBuffer[1] = newQuote;
    return buffer;


}

These are obviously just test values, however if I wanted to specifically make the quote array in the cbuf struct start out at a size of 10, would I declare that in the .h file as:

typedef struct{

    unsigned int testNum;
    quote quoteBuffer[10];

}cbuf;

or in the .c file some other way?

user2252004
  • 49
  • 1
  • 6
  • What do you mean by "start out at a size 10"? Do you imply that you may wish to enlarge this array at the run-time? – Joker_vD Apr 29 '13 at 08:33
  • when you provifde the definition of the struct only you should give the array its size, you cant have it empty. if you dont know the size that will be required then declare a pointer to that type and allocate the "array" on heap – Koushik Shetty Apr 29 '13 at 08:34
  • 1
    `buffer->quoteBuffer[1] = newQuote;` in this code you are writing beyond the size of `quoteBuffer` which was zero. (See [this answer](http://stackoverflow.com/a/14643530/912144) on what a flexible array member is). – Shahbaz Apr 29 '13 at 08:34
  • Also, [you don't cast the result of malloc](http://stackoverflow.com/a/605858/912144) (or calloc) – Shahbaz Apr 29 '13 at 08:36
  • yes I wish to be able to enlarge the size of the array at runtime. I realize that my current code is writing beyond the size of the quoteBuffer when it was zero, however this was just a test to see if the struct was working properly – user2252004 Apr 29 '13 at 08:40

3 Answers3

2

There are two ways of having dynamic arrays in structures. The obvious is of course to have it as a pointer, and dynamically allocate (or reallocate) when needed.


The other is to have an array of size 1, and then allocate a larger size than the structure, to accommodate for the array:

typedef struct {
    unsigned int testNum;
    quote quoteBuffer[1];
} cbuf;

cbuf *cbuf_init(const size_t num_quotes) {
    /* Allocate for the `cbuf` structure, plus a number of `quote`
     * structures in the array
     */
    cbuf *buffer = malloc(sizeof(cbuf) + (num_quotes - 1) * sizeof(quote));

    /* Other initialization */

    return buffer;
}

/* If more quotes are needed after initial allocation, use this function */
cbuf *cbuf_realloc(cbuf *buffer, const size_t new_num_quotes) {
    buffer = realloc(buffer, sizeof(cbuf) + (new_num_quotes - 1) * sizeof(quote));

    /* Other initialization */

    return buffer;
}

Now you can use the array as a normal array:

cbuf *buffer = cbuf_init();
buffer->quoteBuffer[5].time = 123;

Note: I only allocate extra space for 9 quote structures, but state that I allocate ten. The reason is that the cbuf structure already contains one quote structure in its array. 1 + 9 = 10. :)

Note 2: I put the quote array in the cbuf structure with one entry already in it for backwards compatibility. Having an array without a size in the structure is quite new in the C world.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • In the typedef for the cbuf struct, you declare quoteBuffer as an array of size 1. Wouldn't this cause errors when trying to insert a quote object into the 3rd, 4th, etc spot? I realize you allocated memory for these 3rd, 4th, etc quotes, but the array was intialized without spaces for them, no? – user2252004 Apr 29 '13 at 08:44
  • @user2252004 Not if you allocate extra space for it, as shown in my example code. – Some programmer dude Apr 29 '13 at 08:46
  • Ah ok. So in C, if you allocate extra space for an array after you initialize it as a certain size, you can dynamically add/remove indices (spaces) from that array? – user2252004 Apr 29 '13 at 08:48
  • @user2252004 Well in a way, you still only have the space you allocated for it, in my example ten entries in the array. If you need to add more than those initial ten at a later time you could use `realloc` for it. – Some programmer dude Apr 29 '13 at 08:50
  • @user2252004 Just added a function to dynamically reallocate the `cbuf` structure. – Some programmer dude Apr 29 '13 at 08:53
  • While the hack with `quote quoteBuffer[1];` and `buffer = realloc(buffer, sizeof(cbuf) + (new_num_quotes - 1) * sizeof(quote));` tends to work, it involves undefined behaviour. Using a flexible array member is the official standard-compliant way to do it. – Daniel Fischer Apr 29 '13 at 09:16
1

you can also do this if you want 10 quotes in a cbuf but a statically allocated like quote buffer[10] would work too:

cbuf* cbuf_init(int numQuotes)
{
    cbuf *b = calloc(1, sizeof(cbuf) + numQuotes * sizeof(quote));

    return b;
}
Serve Laurijssen
  • 9,266
  • 5
  • 45
  • 98
1

If you want a statically sized circular buffer then your could declare the size in the header file. Using a #define for the buffer size will make the code more readable and maintainable, as you'll reference the size elsewhere in your code.

If you want the circular buffer to be growable then define the size in your C file. You'll then have to take care of tracking the size and destroying the memory that you will have to allocate dynamically.

In your example, I think you need to allocate more room for your quote structs...

cbuf *buffer = (cbuf *)calloc(1,sizeof(cbuf) + NUM_QUOTES*sizeof(struct quote));
                                             ---------------------------------

The reason for this is that in your struct def...

quote quoteBuffer[];

... quoteBuffer doesn't add size to the struct. quoteBuffer will point to one byte past the end of the struct, hence the need to allocate memory for the struct + memory for the array.

EDIT: Daniel Fischer's comment (thanks Daniel) - quoteBuffer may, in some cases, add size to the struct if it introduces padding. The reason is that the compiler will probably strive to get the most optimal alignment for quoteBuffer. For example, ints normally aligned of 4-byte boundaries. E.g. a struct like:

struct {
   char a;
   int b;
}

is probably changed by compiler to

struct {
   char a;
   char pad[3]; // compiler adds padding behind the scenes 
   int b; // align b on a 4-byte boundary
}

This probs doesn't apply in your case as your struct leaves quoteBuffer[] on a 4 byte boundary.

The reason that the compiler does this is two fold. 1. On some architectures (not so common nowadays I think?), unaligned accesses aren't supported. 2. Aligned accesses are more efficient, even if architecture allows non-aligned accesses as it is one memory read as opposed to two memory reads plus a manipulation.

Jimbo
  • 4,352
  • 3
  • 27
  • 44
  • "quoteBuffer doesn't add size to the struct." It might be worth mentioning that it may cause padding to be included that wouldn't be present without the flexible array member. – Daniel Fischer Apr 29 '13 at 09:19
  • @Daniel, yes good point, I hadn't thought of that... will add – Jimbo Apr 29 '13 at 09:26