Why is realloc () memory desirable for a growing array of pointers?

Diagram showing the conceptual layout of a data structure

typedef struct {
    void **head;
    size_t used_size;
    size_t free_size;
    size_t current_size;
    size_t size_increment;
} GrowingArray;

GrowingArray createEmptyGrowingArray(int initial_size, int size_increment) {
    GrowingArray empty_growing_array;
    empty_growing_array.head = malloc(initial_size * sizeof(void *));
    empty_growing_array.used_size = 0;
    empty_growing_array.free_size = initial_size;
    empty_growing_array.current_size = initial_size;
    empty_growing_array.size_increment = size_increment;

    return empty_growing_array;
}

GrowingArray appendToGrowingArray(GrowingArray growing_array, void *new_element) {

    void *new_head_of_array;

    if (growing_array.free_size == 0) {
        new_head_of_array = realloc(growing_array.head, (growing_array.current_size + growing_array.size_increment) * sizeof(void*));
        if (new_head_of_array == NULL) {
            printf("Reallocation failure.\n");
        }

        growing_array.free_size = growing_array.size_increment;
        growing_array.current_size += growing_array.size_increment;
        growing_array.head = new_head_of_array;
    }

    growing_array.head[growing_array.used_size++] = new_element;
    growing_array.free_size--;

    return growing_array;
}

void finalizeGrowingArrayMemory(GrowingArray growing_array) {
    growing_array.head = realloc(growing_array.head, growing_array.current_size * sizeof(void *));
}

void freeGrowingArray(GrowingArray growing_array) {
    free(growing_array.head);
}

int main(int argc, char* argv[]) {
    GrowingArray test_array = createEmptyGrowingArray(5, 1);

    int *test_integer = (int *)malloc(1 * sizeof(int));
    *test_integer = 4;

    int *another_integer = (int *)malloc(1 * sizeof(int));
    *another_integer = 6;

    int *yet_another_integer = (int *)malloc(sizeof(int));
    *yet_another_integer = 9;

    test_array = appendToGrowingArray(test_array, test_integer);
    test_array = appendToGrowingArray(test_array, another_integer);
    test_array = appendToGrowingArray(test_array, yet_another_integer);
    finalizeGrowingArrayMemory(test_array);

    printf("%x,", *(int *)test_array.head[0]);
    printf("%x,", *(int *)test_array.head[1]);
    printf("%x\n", *(int *)test_array.head[2]);

    freeGrowingArray(test_array);

    printf("Going to free %llx\n", (long long int)test_integer);
    free(test_integer);

    printf("Going to free %llx\n", (long long int)another_integer);
    free(another_integer);

    printf("Going to free %llx\n", (long long int)yet_another_integer);
    free(yet_another_integer);

    return 0;
}

      

I wrote this code based on the example code provided in the correct answer to this question: How can I implement a generic dynamically growing array in C?

Included in the answer provided is a function that simply reallocates an array of pointers. The intended use is that it is called after multiple elements have been added to the array as shown in the answer.

I want to know why this should be done. What is the use of this? Does realloc () represent predictions about how a block of memory will be used based on its previous use and then move it where it best fits?

Thank!

As a complement yes or no: should I use calloc()

instead malloc()

internally createEmptyGrowingArray()

?

+3


source to share


1 answer


I want to know why this should be done. What is the use of this?

I think you are asking why there is a function for it or why there is a custom increment:

  • It is convenient to split the reallocation data into your area to avoid cluttering your code, and also provide a reusable resize operation that you can call from other forms of "plug-in" calls that you might implement.
  • Having an increment configuration allows the array to grow by more than one element, which prevents over-reallocation.
  • While your example does not, it is useful to increase the increment size as well. This would allow constant amortized time complexity for array growth.

Does realloc () help to make predictions about how a block of memory will be used based on its previous use and then move it where it best fits?



No, he doesn't know what your block is being used for. It just allocates a chunk of memory. If it can resize without moving memory, it will. But if there is not enough space, or it needs to be moved to a different layout (memory managers often divide blocks into different areas depending on their size), then the pointer will move, and the contents of this memory will be copied to a new location, etc.

As a complement to the yes or no question, should I use calloc () instead of malloc () inside createEmptyGrowingArray ()?

Reasonable to use calloc

, but when creating an empty array remember that you are setting the current size to zero, so it malloc

will suffice because you are not going to use those uninitialized values ​​until you initialize them later (after adding). Also, after a call realloc

to grow a block, you are not currently zeroing out the pointers in the new memory so that it does not enforce conceptual parity on your "create" operation.

So, in your case, I would say use malloc

one that won't give unrealistic expectations about the state of your unused data. If you want to use calloc

on creation, then you should also use memset

to zero out new memory after growth.

+4


source







All Articles