2

Suppose to have a struct that contains a pointer to an array and its size, like this one:

typedef struct {
    int * array;
    int arr_size;
}IntArray;

and want to have this inside another struct, it can be done in two ways:

typedef struct{
    IntArray ia;
    //other variables
}Base1;

typedef struct{
    IntArray * ia;
    //other variables
}Base2;

What happens when I dynamically allocate Base1 and Base2 (e.g Base1 b1 = (Base1 *)malloc(sizeof(Base1));) and why should I choose one way instead of the other?

riciloma
  • 1,456
  • 2
  • 16
  • 31
  • "it can be done in two ways" - those two different structs are very, very different. – Dai Jul 21 '18 at 22:09
  • 2
    Always strive to use less pointers. If you can explain why you absolutely need a pointer, great. If you can't, you don't need it. Thus, Base1 unless you need Base2 for some specific reason you can defend. – n. m. could be an AI Jul 21 '18 at 22:17
  • @n.m. and if `array` has a known size then the array can exist in-place too: `int array[1234]` which avoids dynamic allocation (or pointing to a stack-allocated array) entirely. – Dai Jul 21 '18 at 22:18

2 Answers2

3
  1. Nested structs' space exist as space in their parent struct, which means they don't need their own allocation (but they might still need their own initialization), whereas struct fields that are pointers need to be both allocated and freed when the parent object is initiated (this is a common cause of memory leaks in C because it does not have automatic object destructors like C++ does). Though if using a pointer you could point to another array/object that might exist on the stack (thus avoiding malloc/free) but then you might run into object lifetime bugs depending on the difference on scope and lifetimes of your objects.

  2. Nested structs exist in-place, so they cannot be shared by other instances. This may or may not be ideal (you could solve this with a template in C++, in C you'd have to settle for a hideous preprocessor macro).

  3. Because dynamically-allocated objects (such as your array and your Base2 type's nested ia member) exist in different locations in physical memory it means your code will not take advantage of spatial locality that the CPU's caches can take advantage of and you'll incur a double pointer dereference. So your code will run slower.

Anyway: when in C, you should generally try to minimize pointer use.

Dai
  • 141,631
  • 28
  • 261
  • 374
1

Basically the question is the same as, should I allocate a struct or a pointer to a struct? That is:

IntArray myStruct;

or

IntArray *myStructPtr;

The fact that the variables in question are within a struct makes no difference, you can choose either.

And you access them in the same manner as you would if they were not inside another structure, after referencing the field inside the outside structure of course, so

Base1 contains the actual IntArray struct so you would

Base1 *b1 = malloc(sizeof(*b1));
b1->ia.array = malloc(yourSizeHere);

Base2 contains a pointer to a IntArray struct, so you would need to point it to an existing IntArray struct or malloc() memory for it, and then access it as a pointer.

Base2 *b2 = malloc(sizeof(*b2));
b2->ia = malloc(sizeof(*(b2->ia)));
b2->ia->array = malloc(yourSizeHere);
Stephen Docy
  • 4,738
  • 7
  • 18
  • 31
  • As C doesn't have access modifiers it raises the question of who "owns" the inner pointed-to objects and who is responsible for calling `free` on those inner objects. With by-value members it's automatic. – Dai Jul 21 '18 at 22:23