-1

I want to implement a simple database in C, and I need to use an array of structs and realloc it when resizing is needed.
After continuous efforts, I realized that I cannot use malloc for an array of structs because malloc returns a pointer, so I cannot have a struct on the left side of the equal symbol. So my question is:

How can you malloc an array of structs?

qiu
  • 121
  • 7
  • 1
    It *is* possible, please show what you tried. – Weather Vane Mar 29 '21 at 16:26
  • "I cannot use malloc for an array of structs because malloc returns a pointer" -- arrays are pointers, what's the problem? – Blindy Mar 29 '21 at 16:28
  • The syntax for accessing an array of structs is `array[index].struct_member`. – Devolus Mar 29 '21 at 16:29
  • I know that there are a lot of forum and even SO questions that refer to the same project, but I am not really satisfied with the answers since the majority of the questions avoid the use of type-casting before the "malloc" keyword. What is the benefit of it ? I was told to always use type casting before malloc. – qiu Mar 29 '21 at 16:30
  • @qiu whether the answers use the cast or not shouldn't really affect whether they are right or not. Also, see [Do I cast the result of malloc?](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – mediocrevegetable1 Mar 29 '21 at 16:32
  • @qiu can't say for certain without seeing the code, by my crystal ball tells me you are referring to constructs such as `Foo* f = malloc(...)`. This is actually a correct and idiomatic way of using `malloc` in C. – SergeyA Mar 29 '21 at 16:33
  • The most promising idea is to allocate memory for an array of pointers to the structs. The benefit of that is that such an array can be resized pretty easily but the problem is that if you don't create an array of structs you won't have any instances to store the data into. On the other hand, if you do create an array of structs it will be fixed- size because malloc only returns pointers. – qiu Mar 29 '21 at 16:33
  • An array of pointers is uneccessary complex in your case. – Devolus Mar 29 '21 at 16:34
  • @SergeyA , yes your are getting it. The problem occurs when you will try accessing the members of the structs that are the elements of the array. – qiu Mar 29 '21 at 16:35
  • @Devolus I agree, cause I read that you can have one pointer that will refer to a different struct each time. The problem is that I cannot get the struct into a dynamic allocated array. – qiu Mar 29 '21 at 16:37
  • You just allocate `sizeof(struct) * N` items, and access it via the base pointer as I already wrote above. And if your N grows, you have to reallocate. – Devolus Mar 29 '21 at 16:39
  • @Devolus Can you write for me the complete line of malloc? – qiu Mar 29 '21 at 16:42

2 Answers2

2

Given a pointer p to the first structure of an array of structures, you can access the structure with index i using p[i].

You can allocate and initialize an array of structures like this:

#include <stdio.h>
#include <stdlib.h>


typedef struct { int MyInteger; } MyStructure;


MyStructure *GiveMeAnArrayOfStructures(size_t NumberOfStructures)
{
    //  Allocate memory.
    MyStructure *p = malloc(NumberOfStructures * sizeof *p);
    if (!p)
    {
        fprintf(stderr, "Error, unable to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    //  Initialize structures.
    for (size_t i = 0; i < NumberOfStructures; ++i)
        p[i].MyInteger = i;

    //  Return array, via pointer to first element.
    return p;
}

This example uses a structure member on the left side of an assignment, but we can also use a structure on the left side:

    for (size_t i = 0; i < NumberOfStructures; ++i)
    {
        MyStructure Example = { i };
        p[i] = Example;
    }
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • isn't p a pointer of type MyStructure* ? I mean, I don't think that p is a structure on it's own and this would become even more obvious if we try to access or print one of its members. – qiu Mar 29 '21 at 16:50
  • @qiu: `p` is a pointer to a `MyStructure`. So? `p` is not used by itself on the left side of an assignment, except when it is initially assigned from `malloc`. One of the examples shown is `p[i] = Example;`. There the left side of the assignment is `p[i]`, not `p`. `p[i]` is a structure. – Eric Postpischil Mar 29 '21 at 16:51
  • @EricPostpischil , I appreciate your interest and well-written answer but there are a couple of things I don't understand. The first is the line ``` MyStructure Example = { i };``` At first glance, I thought that something is off with the assignment cause i is of type int and Example is a struct. What do these brackets do there? Because I assume there is a reason for them to be there that I don't seem to understand. – qiu Mar 29 '21 at 17:35
  • @EricPostpischil ,Secondly, I thought that according to Sequential locality, "the same type of data is meant to be stored in solid blocks of memory that are consisted of adjacent virtual memory addresses". By that, I thought that if the first element of an array is a pointer, then the whole array will be of type pointer. Similarly, if the first element is a struct then every element of the array would be a struct. As it seems, I haven't interpreted that pretty accurately. Any help on that please ? – qiu Mar 29 '21 at 17:36
  • @qiu: In `MyStructure Example = { i };`, `MyStructure Example` defines an object of type `MyStructure` named `Example`, and `= { i }` says to initialize it. The braces indicate the start of values for a structure (or other aggregate), and `i` is a value that goes into the first member of the structure. (For history reasons, a scalar—single objects, not aggregates—can also be initialized uses braces, but that is a special case.) – Eric Postpischil Mar 29 '21 at 18:00
  • @qiu: An array is not a pointer. The first element of an array of `MyStructure` is not a pointer; it is a `MyStructure`. The call `malloc(NumberOfStructures * sizeof *p)` allocates enough memory for an array of objects of type `*p` (which is a `MyStructure`), with the number of elements in the array to be `NumberOfStructures`. This call returns a pointer which is assigned to `p`. Then `p` points to that array (equivalently, the memory for that array). There is no pointer in the memory. `p` points to the memory. `p[0]` is the first structure in that memory. `p[1]` is the second structure. – Eric Postpischil Mar 29 '21 at 18:03
1

Assume a struct type named struct s, and you want to allocate space for N instances:

struct s *arr = malloc( sizeof *arr * N );

Graphically:

      struct s *    struct s
      +---+         +---+
 arr: |   | ------> |   | arr[0]
      +---+         +---+
                    |   | arr[1]
                    +---+
                     ...
                    +---+
                    |   | arr[N-1]
                    +---+

In other words, malloc reserves enough space to store N structs, and returns a pointer to the beginning of that space, which gets stored in arr. You can use the subscript operator [] on pointers as well as array expressions, so you'd refer to each struct as arr[i] and access members as arr[i].member.

the majority of the questions avoid the use of type-casting before the "malloc" keyword. What is the benefit of it ? I was told to always use type casting before malloc.

In the earliest versions of C (known as K&R C) the void type did not exist, and there was no way to declare a "generic" pointer like void *. In those days, malloc (and calloc and realloc) returned a char *, so if you wanted to assign a result to something that wasn't a char *, you had to cast the result:

/** K&R C */
int *ptr = (int *) malloc( sizeof *ptr * N );

That changed in the 1989 standard, which introduced the void type and created the rule that void * could be directly assigned to other pointer types, making the cast redundant. So from C89 onward, you can write:

/** C89 and later */
int *ptr = malloc( sizeof *ptr * N );

The cast is no longer necessary, and most of us recommend leaving it off because it only adds clutter and the potential for making mistakes (and under the C89 standard could suppress a useful diagnostic if you forgot to #include <stdlib.h>). The only times you must cast the result of malloc are:

  • You are using an ancient K&R compiler;
  • You are compiling the code as C++;

Unlike C, C++ does not allow direct assignment between void * and other pointer types, and requires you to use a cast. But if you're writing C++, you shouldn't be using malloc anyway.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
John Bode
  • 119,563
  • 19
  • 122
  • 198