Registered User
Registered User

Reputation: 2299

Can one element in struct access another element of itself in C?

I want to declare a int num in struct S. Then the same struct should also have a array B of size num(So B will access num from it's own struct).

while in a function, I can do,

func(int A)
{
    int max=A; //I could use A directly, I am just trying to explain my plan.
    int B[max];
}

same won't work for struct as such,

struct S {
    int num;
    int data[num]; //this is wrong, num is undeclared
};

Is there any way I can do this?

Upvotes: 6

Views: 2908

Answers (4)

Paul Ogilvie
Paul Ogilvie

Reputation: 25286

The reason the compiler complains when you "flexibly declare" the array in the struct in global memory, is because global memory can only be allocated (declared) at compile-time and at compile time all sizes must be known. (The value of a variable is not known at compile time by definition.)

The reason it accepts a flexible array in a function, is because the function's local variables are created at the moment the function is entered and then the compiler can accept a variable size. (It boils down to the compiler allocating more memory on the stack and offsetting all accesses to local variables with the size - but different compilers could have a different approach.)

#include <stdio.h>
int size;
struct S {
    int num;
    int a[size];    // illegal: size must be known at compile time
};

int f(int size)
{
    int a[size];   // legal as a is allocated on the stack
    ....
}

The following would be legal:

#include <stdio.h>
#define A_SIZE 10
struct S {
    int num;
    int a[A_SIZE];    // legal: A_SIZE is known at compile time
};

P.s.: I am not a C99 programmer; I may have some mistakes here.

Upvotes: 2

Lundin
Lundin

Reputation: 214515

First of all, the member num is not declared until the end of the struct definition, which ends at the last }.

Second, how would it make sense to set the array size to be the value of an uninitialized variable?

What I think you attempt to do with int B[max] is to create a variable length array (VLA). But that won't work either, as they are explicitly forbidden inside structs. 6.7.2.1/9:

A member of a structure or union may have any complete object type other than a variably modified type.

What you could do instead is to declare a flexible array member, as demonstrated in the answer by Ouah.

Upvotes: 3

John Bode
John Bode

Reputation: 123558

There are several problems with

struct S {
    int num;
    int data[num]; 
};

that cause it to not work the way you want to.

The first is that the num being used in the array member declaration isn't the same num that's the member of the struct type; the compiler treats the num in the array declaration as a regular identifier (i.e., it assumes there's a different variable named num in the same scope as the struct declaration)1.

The second problem is that a struct or union type may not have a variable-length array as a member2. However, the last member in the struct may have an incomplete array type:

struct S {
    int num;
    int data[]; 
};

Unfortunately, you're still kind of stuck here; if you create an instance of struct S like

struct S foo;

it doesn't actually allocate any space for the array. You'd need to allocate the struct dynamically:

/**
 * Note that sizeof doesn't try to actually dereference foo below
 */
struct S *foo = malloc( sizeof *foo + N * sizeof *foo->arr );

to allocate space for the array itself. Note that you cannot declare an array of struct S or use it as a member of another structure or union type if the last member has an incomplete array type. 3

Honestly, your best option is to define the struct as

struct S {
  size_t num;
  int *data;
};

and then allocate the memory for data as a separate operation from allocating memory for the struct object itself:

struct S foo;
foo.num = some_value();
foo.data = malloc( sizeof *foo.data * foo.num );

Since struct S now has a known size, you can declare arrays of it, and you can use it as a member of another struct or union type:

struct S blah[10];

struct T {
  struct S s;
  ...
};


1. C supports four different name spaces - label names (disambiguated by label syntax), struct/union/enum tag names (disambiguated by the presence of the struct, union, or enum keyword), struct and union member names (disambiguated by the . and -> component selection operators), and everything else. Since the num in your array declaration is not an operand of . or ->, the compiler treats it as a regular variable name.

2. 6.7.2.1/9: "A member of a structure or union may have any complete object type other than a variably modified type."

2. 6.2.7.1/3: 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.

Upvotes: 4

ouah
ouah

Reputation: 145899

Use a flexible array member:

struct S {
    int num;
    int data[];
};

int x = 42; 

struct S *p = malloc(sizeof (struct S) + sizeof (int [x]));
p->num = x;

Upvotes: 9

Related Questions