How do I allocate more space for my array of C structs?
Asked Answered
Q

2

8

I'm trying to add 10 more elements to my struct that has been already malloc with a fixed sized of 20. This is the way I have my struct defined:

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

struct st_temp {
   char *prod;
};

int main ()
{
   struct st_temp **temp_struct;

   size_t j;
   temp_struct = malloc (sizeof *temp_struct * 20);
   for (j = 0; j < 20; j++) {
      temp_struct[j] = malloc (sizeof *temp_struct[j]);
      temp_struct[j]->prod = "foo";
   }

   return 0;
}

So what I had in mind was to realloc as (however, not sure how to):

temp_struct = (struct st_temp **) realloc (st_temp, 10 * sizeof(struct st_temp*));

and then add the extra 10 elements,

   for (j = 0; j < 10; j++)
      temp_struct[j]->prod = "some extra values";

How could I achieve this? Any help is appreciated!

Quirt answered 31/10, 2010 at 21:27 Comment(1)
I agree with Daniel... What is the reason for the double pointer indirection? Would it not be simpler to have struct st_temp * temp = malloc( 20 * sizeof *temp ); for (...) { temp[i].prod = "foo"; }?? The least memory that you manage manually, the less error prone the solution is.Lido
E
7

To avoid memory leaks, we need to handle reallocating with care (more on that later). The realloc function:

void *realloc(void *ptr, size_t size), where

ptr = the pointer to the original (malloc'ed) memory block, and

size = the new size of the memory block (in bytes).

realloc returns the new location of the dynamically allocated memory block (which may have changed) - or NULL if the re-allocation failed! If it returns NULL, the original memory stays unchanged, so you must always use a temporary variable for the return value of realloc.

An example will clarify this a bit (points of interest: realloc syntax is similar to malloc's (no need for extra casts etc.) and, after realloc, you need to produce the same steps for the new objects as you did after malloc):

struct st_temp **temp_struct;
temp_struct = malloc(20 * sizeof *temp_struct);
if (temp_struct == NULL) { /* handle failed malloc */ }
for (int i = 0; i < 20; ++i) {
    temp_struct[i] = malloc(sizeof *temp_struct[i]);
    temp_struct[i]->prod = "foo";
}

// We need more space ... remember to use a temporary variable
struct st_temp **tmp;
tmp = realloc(temp_struct, 30 * sizeof *temp_struct);
if (tmp == NULL) { 
    // handle failed realloc, temp_struct is unchanged
} else {
    // everything went ok, update the original pointer (temp_struct)
    temp_struct = tmp; 
}
for (int i = 20; i < 30; ++i) { // notice the indexing, [20..30)
    // NOTICE: the realloc allocated more space for pointers
    // we still need to allocate space for each new object
    temp_struct[i] = malloc(sizeof *temp_struct[i]);
    temp_struct[i]->prod = "bar";
}
// temp_struct now "holds" 30 temp_struct objects
// ...
// and always do remember, in the end
for (int i = 0; i < 30; ++i)
    free(temp_struct[i]);
free(temp_struct);

Do note, that this is not really an array of structs, but more an array of pointers to structs - or even an array of arrays of struct if you wish. In the last case, each sub-array would be of length 1 (since we only allocate space for one struct).

Eames answered 31/10, 2010 at 21:43 Comment(2)
Thanks. Great info!, do you know why I get: assignment from incompatible pointer type when temp_struct = tmp; ?Quirt
I had a typo in the declaration of tmp.Eames
P
9

When you use realloc(), you must give the new size instead of the number of bytes to add. So:

temp_struct = (struct st_temp **) realloc (temp_struct, 30 * sizeof(struct st_temp*));

30 is, of course, your original 20 plus 10 more. The realloc() function takes care of copying the original data to a new location if it needs to move the memory block.

Then, adding the extra 10 elements would be something like (starting at index 20, not 0):

for (j = 20; j < 30; j++) {
    temp_struct[j]->prod = "some extra values"; 
}
Pinson answered 31/10, 2010 at 21:33 Comment(1)
A bit too simple, perhaps? Doubling the original misconception of not having to allocate new objects (realloc does not do that), and no error-handling (somewhat crucial in C).Eames
E
7

To avoid memory leaks, we need to handle reallocating with care (more on that later). The realloc function:

void *realloc(void *ptr, size_t size), where

ptr = the pointer to the original (malloc'ed) memory block, and

size = the new size of the memory block (in bytes).

realloc returns the new location of the dynamically allocated memory block (which may have changed) - or NULL if the re-allocation failed! If it returns NULL, the original memory stays unchanged, so you must always use a temporary variable for the return value of realloc.

An example will clarify this a bit (points of interest: realloc syntax is similar to malloc's (no need for extra casts etc.) and, after realloc, you need to produce the same steps for the new objects as you did after malloc):

struct st_temp **temp_struct;
temp_struct = malloc(20 * sizeof *temp_struct);
if (temp_struct == NULL) { /* handle failed malloc */ }
for (int i = 0; i < 20; ++i) {
    temp_struct[i] = malloc(sizeof *temp_struct[i]);
    temp_struct[i]->prod = "foo";
}

// We need more space ... remember to use a temporary variable
struct st_temp **tmp;
tmp = realloc(temp_struct, 30 * sizeof *temp_struct);
if (tmp == NULL) { 
    // handle failed realloc, temp_struct is unchanged
} else {
    // everything went ok, update the original pointer (temp_struct)
    temp_struct = tmp; 
}
for (int i = 20; i < 30; ++i) { // notice the indexing, [20..30)
    // NOTICE: the realloc allocated more space for pointers
    // we still need to allocate space for each new object
    temp_struct[i] = malloc(sizeof *temp_struct[i]);
    temp_struct[i]->prod = "bar";
}
// temp_struct now "holds" 30 temp_struct objects
// ...
// and always do remember, in the end
for (int i = 0; i < 30; ++i)
    free(temp_struct[i]);
free(temp_struct);

Do note, that this is not really an array of structs, but more an array of pointers to structs - or even an array of arrays of struct if you wish. In the last case, each sub-array would be of length 1 (since we only allocate space for one struct).

Eames answered 31/10, 2010 at 21:43 Comment(2)
Thanks. Great info!, do you know why I get: assignment from incompatible pointer type when temp_struct = tmp; ?Quirt
I had a typo in the declaration of tmp.Eames

© 2022 - 2024 — McMap. All rights reserved.