Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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".

like image 876
Midas Avatar asked Nov 05 '10 14:11

Midas


People also ask

Can a struct variable be an array?

Structure is a data type that gives us the ability to allow a group of interconnected variables to be thought of as one unit instead of distinct entities. A structure may contain different data types – char, int, double, float, etc. It may also include an array as its member.

Can we declare array inside structure?

For the structures in C programming language from C99 standard onwards, we can declare an array without a dimension and whose size is flexible in nature. Such an array inside the structure should preferably be declared as the last member of structure and its size is variable(can be changed be at runtime).

Can I use struct as array?

An array of structres in C can be defined as the collection of multiple structures variables where each variable contains information about different entities. The array of structures in C are used to store information about multiple entities of different data types.

What is a variable length array in C?

A variable length array, which is a C99 feature, is an array of automatic storage duration whose length is determined at run time.


2 Answers

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);
like image 186
frast Avatar answered Sep 27 '22 18:09

frast


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.

like image 27
Jonathan Leffler Avatar answered Sep 27 '22 16:09

Jonathan Leffler