c++ Initializing a struct with an array as a member

84,264

Solution 1

c++ doesn't have the same flexible array member as last element as c99. You should use a std::vector if you don't know how many elements or you should specify how many if you do.

EDIT: You have said in your edit that the array is a runtime constant, so specify the size and it should work fine. g++ has no problem with the following code:

struct TestStruct { // note typedef is not needed */
    int length;
    int values[3]; // specified the size
};

TestStruct t = {3, {0, 1, 2}};

int main() {
    // main implicitly returns 0 if none specified
}

EDIT: to address your comment, you could use templates like this:

template <int N>
struct TestStruct {
    int length;
    int values[N];
};

TestStruct<3> t3 = {3, {0, 1, 2}};
TestStruct<2> t2 = {2, {0, 1}};

int main() {}

The only problem is that there is no easy way to put both t2 and t3 in a container (like a list/vector/stack/queue/etc because they have different sizes. If you want that, you should use std::vector. Also, if you are doing that, then it isn't necessary to store the size (it is associated with the type). So you could do this instead:

template <int N>
struct TestStruct {
    static const int length = N;
    int values[N];
};

TestStruct<3> t3 = {{0, 1, 2}};
TestStruct<2> t2 = {{0, 1}};

int main() {}

But once again, you cannot put t2 and t3 in a "collection" together easily.

EDIT: All in all, it sounds like you (unless you store more data than just some numbers and the size) don't need a struct at all, and can't just use a plain old vector.

typedef std::vector<int> TestStruct;


int t2_init[] = { 0, 1, 2 };
TestStruct t3(t3_init, t3_init + 3);

int t2_init[] = { 0, 1 };
TestStruct t2(t2_init, t2_init + 2);

int main() {}

Which would allow you to have both t2 and t3 in a collection together. Unfortunately std::vector doesn't (yet) have array style initializer syntax, so i've used a shortcut. But it's simple enough to write a function to populate the vectors in a nice fashion.

EDIT: OK, so you don't need a collection, but you need to pass it to a function, you can use templates for that to preserve type safety!

template <int N>
struct TestStruct {
    static const int length = N;
    int values[N];
};

TestStruct<3> t3 = {{0, 1, 2}};
TestStruct<2> t2 = {{0, 1}};

template <int N>
void func(const TestStruct<N> &ts) { /* you could make it non-const if you need it to modify the ts */
    for(int i = 0; i < N; ++i) { /* we could also use ts.length instead of N here */
        std::cout << ts.values[i] << std::endl;
    }
}

// this will work too...
template <class T>
void func2(const T &ts) { 
    for(int i = 0; i < ts.length; ++i) {
        std::cout << ts.values[i] << std::endl;
    }
}

int main() {
    func(t2);
    func(t3);
    func2(t2);
}

Solution 2

GCC/Clang support the following extension


    typedef struct TestStruct
    {
        int length;
        int* values;
    };

    TestStruct t = {3, (int[]){0, 1, 2}};
    TestStruct t2 = {4, (int[]){0, 1, 2, 3}};

Solution 3

struct TestStruct {
    int length;
    const int *values;
};

static const uint8_t TEST_STRUCT_VAL_1[] = {0, 1, 2};
const TestStruct t1 = {3, TEST_STRUCT_VAL_1};

static const uint8_t TEST_STRUCT_VAL_2[] = {0, 1, 2, 3};
const TestStruct t2 = {4, TEST_STRUCT_VAL_2};`

In my case, stm32 programming with gcc/g++, the value array is only raw data storage, so it is static const, and would be stored in flash. I use this way to store font patterns for lcd display.

using template is typing saving, but not size saving.

Share:
84,264
Drew Shafer
Author by

Drew Shafer

Head of Engineering at Toopher, Inc.

Updated on April 12, 2020

Comments

  • Drew Shafer
    Drew Shafer about 4 years

    Edited again because it originally wasn't clear that I'm trying to initialize the arrays at compile time, not at run time...


    I've got the following reduced testcase:

    typedef struct TestStruct
    {
        int length;
        int values[];
    };
    
    TestStruct t = {3, {0, 1, 2}};
    TestStruct t2 = {4, {0, 1, 2, 3}};
    
    int main()
    {
        return(0);
    }
    

    This works with Visual C++, but doesn't compile with g++ under linux. Can anyone help me make this specific kind of initializer portable?

    Additional details: the actual structure I'm working with has several other int values, and the array can range in length from a single entry to over 1800 entries.

    EDIT: I think (but am not sure) that this is not a VLA issue. To clarify, I'm trying to get the compiler to do the work for me at compile-time. The length of the array at run-time is constant. Apologies if I'm wrong; I'm primarily a c#/Perl/Ruby programmer who is stuck maintaining this legacy app...

    Any help much appreciated. Thanks!

  • Drew Shafer
    Drew Shafer about 14 years
    Can you specify an array size in a typedef? In any case, I'm declaring several instances of this struct, each with a different length array.
  • Drew Shafer
    Drew Shafer about 14 years
    Brilliant! Thanks - this solves my problem! Thanks for the warning about templates not being the same type - I don't need a container of them, but I do need to pass them to a common function. Void pointers and typecasting should do the trick there...
  • Drew Shafer
    Drew Shafer about 14 years
    A note about why I can't just use vectors - I'm actually storing some information about CPU registers that need to be read for different architectures. In addition to the array of register addresses I need to read, there are about 8 other 32-bit integers needed to specify how to access each series of registers. These other ints got "optimized out" when I was creating my (rather silly) test case.
  • Jason
    Jason about 14 years
    If you want to pass the templated version to a function, make the function a template too! I'll edit in a version with that :-).
  • Drew Shafer
    Drew Shafer about 14 years
    Ok. Now I just want to buy you a beer.
  • adelphus
    adelphus over 7 years
    This will only work at the static/global level. If you do this within a method, the compiler will generate a temporary array of ints, assign it to the struct and then immediately destroy the array, leaving the struct with a bad pointer; GCC warns if you try to do this.
  • passionateProgrammer
    passionateProgrammer almost 4 years
    can't we have the following? int array[3]{4,6,9}; TestStruct t = {3, array};