Midas
Midas

Reputation: 7131

Variable as array size in struct

I implement a file's struct in my program but for some arrays in the struct I don't know the size. The size of the array is stored in another variable but it's unknown before the struct is filled in.

struct Vertex {
    float x;
    float y;
    float z;
};
struct myFile {
    ulong nVertices;
    Vertex vertices[nVertices];
};

That gives an error: "error C2065: 'nVertices' : undeclared identifier".

Upvotes: 9

Views: 7622

Answers (2)

frast
frast

Reputation: 2740

You should store a pointer in your struct:

Vertex *vertices;

Then allocate the memory at runtime:

myFile f;
f.vertices = malloc(nVertices * sizeof(Vertex));
if (f.vertices == 0)
    handle_out_of_memory();

f.nVertices = nVertices;

Remember to free the memory when done:

free(f.vertices);

Upvotes: 14

Jonathan Leffler
Jonathan Leffler

Reputation: 753565

C99 introduces 'flexible array members', which may be what you want to use. Your code still ends up looking remarkably like the code suggested by @frast, but is subtly different.

§6.7.2.1 Structure and union specifiers

A structure or union shall not contain a member with incomplete or function type (hence, a structure shall not contain an instance of itself, but may contain a pointer to an instance of itself), except that the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

[...]

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. With two exceptions, the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length.106) Second, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

EXAMPLE Assuming that all array members are aligned the same, after the declarations:

struct s { int n; double d[]; };
struct ss { int n; double d[1]; };

the three expressions:

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

have the same value. The structure struct s has a flexible array member d.

If sizeof (double) is 8, then after the following code is executed:

struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);

and assuming that the calls to malloc succeed, the objects pointed to by s1 and s2 behave as if the identifiers had been declared as:

struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;

Following the further successful assignments:

s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);

they then behave as if the declarations were:

struct { int n; double d[1]; } *s1, *s2;

and:

double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior

The assignment:

*s1 = *s2;

only copies the member n and not any of the array elements. Similarly:

struct s t1 = { 0 }; // valid
struct s t2 = { 2 }; // valid
struct ss tt = { 1, { 4.2 }}; // valid
struct s t3 = { 1, { 4.2 }}; // invalid: there is nothing for the 4.2 to initialize
t1.n = 4; // valid
t1.d[0] = 4.2; // undefined behavior

106) The length is unspecified to allow for the fact that implementations may give array members different alignments according to their lengths.

The example is from the C99 standard.

Upvotes: 10

Related Questions