0

I have a struct that looks like this.

struct MyStruct1 {
    int (*fn)();
    int b;
}

And another struct that looks like this.

struct MyStruct2 {
    int a;
    struct MyStruct1 b[0];
}

I would like to declare a global variable of type MyStruct2, somewhat like this.

int func1() { return 1; }
int func2() { return 2; }
struct MyStruct2 a = { 1, { func1, 5 }, { func2, 6 } };

However, I get a "Initializer Element is not a compile-time constant".

I would like to know (a) if it is even possible to globally declare a variable sized struct (or at least define a chunk of space, of the correct size, to have the values inserted into later), and (b) if it is, what am I doing wrong?

Jack Maloney
  • 527
  • 2
  • 5
  • 18
  • 3
    the compiler is going to want to allocate space for your structure at the time it's compiling the code. Since you can't tell it how big you structure is, since that will only happen at execution time, what you want is literally impossible. the compiler cannot time-travel. You'd have to simply put in a pointer for your struct, and then malloc the appropriate space later on. – Marc B Jan 06 '15 at 21:55
  • 1
    Perhaps [flexible arrays](http://stackoverflow.com/q/20221012/1708801) could be an option. – Shafik Yaghmour Jan 06 '15 at 21:56
  • @MarcB - So there is no way to tell the compiler that the `a` should have space for x `MyStruct1`s. Or even to allocate a space of the appropriate size, to have the values inserted into later? – Jack Maloney Jan 06 '15 at 22:03
  • you can give it the max possible size you'd ever want, but if you rarely ever use that much, then you'll be wasting a ton of ram reserving space for something that almost never happens. That's why there's malloc() - dynamically allocate memory at runtime, as needed. – Marc B Jan 06 '15 at 22:04

2 Answers2

0

You cannot create arrays of size 0 in C legitimately. In C99 or C11, you can use a 'flexible array member' like this:

struct MyStruct2 {
    int a;
    struct MyStruct1 b[];
};

but structures that have a flexible array member can only usefully be created with dynamic memory allocation (other forms of allocation give you an unusable flexible array of size 0).

The older 'struct hack' version of a structure with a variable size array uses an array of size 1 in the structure. You can create global versions of such a structure with an array of size 1.

But basically, you are trying to do what the language prohibits you from doing, and not very surprisingly, you are failing.

What you do about this depends on what you need. Global variables are inherently somewhat undesirable, so there's an element of "you should be trying to avoid doing this". That said, the rules apply to file scope (static) variables too, and those have many uses.

You can use an explicit pointer in place of the array, and have separate allocations of the body of the struct MyStruct2 and its array of struct MyStruct1 members. You can forgo the global variable and use dynamically allocated structures with a flexible array member.

struct MyStruct2 *ms2 = malloc(sizeof(*ms2) + N * sizeof(ms2->b[0]));

This creates an struct MyStruct2 (as shown at the top of this answer) with N members in the array. Without any further changes, you can use ms2->b[0] through ms2->b[N-1] (well, apart from error checking that the malloc() succeeded).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

It is not possible to declare a variable-size struct, neither locally nor globally. Every type has a fixed size determined at compile time.

The error message you report is surprising, however. If all the declarations you gave are at file scope, in the same file, in the order you gave them, then the initializer for variable a is a compile-time constant. However, it is not a proper initializer for a struct MyStruct2,

  • because it specifies more elements than that struct type has members,
  • because the initializer element for a.b is an initializer for a struct MyStruct1 instead of for an array of such, and
  • because even if you converted the last two initializer elements into one array initializer, it has more elements than there are elements in a.b (i.e. more than zero).

If you want a dynamically-sized array, whether as a variable in its own right or as a member of a struct, then you must declare a pointer to it, and allocate memory for the elements dynamically. In that case, the elements are not themselves part of the struct; only the pointer to them is. (That is different, by the way, from a fixed size array whose size is implicit in its initializer; these are possible only for independent types, though, not for types of struct or union members).

EDIT: C99 flexible arrays are a possible alternative, as ShafikYaghmour commented. These are similar, but not identical, to a struct element that is a pointer to a dynamically-allocated array. In that case, however, you not only cannot statically declare the array elements, you also cannot statically declare instances of the struct itself, so this wouldn't at all get around your initializer issue. There are several other quirks and limitations. Personally I see few advantages to flexible arrays, but they do make it a bit easier to properly free struct instances.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157