dastrobu
dastrobu

Reputation: 1736

array of dynamically declared struct

Suppose we want to construct an array of structs, where the definition of the struct cannot be known at compile time.

Here is a SSCCE:

#include <stdlib.h>

int main(int argc, char *argv[]){
    if (argc < 3) return 1;
    int n = atoi(argv[1]);
    int k = atoi(argv[2]);
    if ((n < 1) || (k < 1)) return 2;

    // define struct dynamically
    typedef struct{
        int a[n];
        short b[k];
    }data_point_t;

    int m = 10;
    // construct array of `m` elements
    data_point_t *p = malloc(sizeof(data_point_t)*m);

    // do something with the array
    for(int i = 0; i < m; ++i) p[i].a[0] = p[i].b[0] = i; 
    free(p);
    return 0;
}

This works fine with gcc (C99), however it doesn't with clang, which yields:

error: fields must have a constant size: 
       'variable length array in structure' extension will never be supported

So I'm obviously relying on a gcc extension. My question is, how to deal with this kind of problem in standard conform C99? (Bonus question: how to do this in C++11?)

Note: Performance matters, when iterating p there should be aligned memory access. Dereferencing pointers in the loop, yielding random memory access, is not an option.

Upvotes: 2

Views: 171

Answers (3)

unwind
unwind

Reputation: 400109

I think your best bet is to drop the idea of wrapping the array in a structure, bite the bullet and allocate a 2D array yourself.

This will mean that you need to do explicit indexing, but that would have to happen under the hood anyway.

When it comes to alignment, if you're going to visit every n array elements in each of the m arrays, it probably doesn't matter, it's better to make them compact to maximize use of cache.

Something like:

int *array = malloc(m * n * sizeof *array);

Then to index, just do:

// do something with the array
for(int i = 0; i < m; ++i)
{
  for(int j = 0; j < n; ++j)
    array[i * n + j] = j;
}

If you're very worried about that multiplication, use a temporary pointer. After profiling it, of course.

Sometimes you see this done with a helper macro to do the indexing:

#define INDEX(a, n, i, j)  (a)[(i) * (n) + (j)]

then you can write the final line as:

INDEX(array, n, i, j) = j;

it's a bit clumsy since the n needs to go in there all the time, of course.

Upvotes: 2

Lundin
Lundin

Reputation: 215115

First of all, it only makes sense to wrap the array inside a struct in the case there are other struct members present. If there are no other struct members, simply allocate an array.

If there are other struct members, then use a flexible array member to achieve what you want. Flexible array members are well-defined in the C standard and will work on every C99 compiler.

// define struct dynamically
typedef struct{

    type_t the_reason_you_need_this_to_be_a_struct_and_not_an_array;
    int a[]; // flexible array member
}data_point_t;

// construct array of `m` elements
int m = 10;
size_t obj_size = sizeof(data_point_t) + n*sizeof(int);
data_point_t *p = malloc(m * obj_size);

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409472

In C++ you can of course use pointers much like you do now, but for a "proper" C++ solution the only viable solution is to use std::vector:

struct data_point_t
{
    explicit data_point_t(const size_t sz)
        : a(sz)  // Construct the vector `a` with `sz` entries,
                 // each element will be zero initialized (`int()`)
    {}

    std::vector<int> a;
};

int main(int argc, char *argv[]){
    // Read `n`...
    int n = 10;  // Just example

    // Read `m`...
    int m = 10;  // Just example

    // Construct vector of `m` elements
    std::vector<data_point_t> p(m, data_point_t(n));

    // Here the vector `p` contains `m` elements, where each instance
    // have been initialized with a vector `a` with `n` elements
    // All fully allocated and initialized

    // Do something with the array
    // ...
}

This is valid C++03 code, so unless you use something ancient (like Turbo C++) any compiler today should support it.

Upvotes: 0

Related Questions