Home > Back-end >  How to pop a dynamically allocated array in C
How to pop a dynamically allocated array in C

Time:03-31

I am trying to produce a library that contains C functions to create a dynamically allocated array as well as other basic array operations. I have defined a typedef struct that contains a pointer variable array which stores the data points in the array. The struct also contains other variables such as the allocated array size the array length (i.e. len) as well as the array name and datatype. The goal is that the array should be dynamically allocated and the array container should be able to hold any data type. I have created a function titled init_array, which is a wrapper around array_mem_alloc to instantiate the array container. Finally I have another function titled append_array where the user can pass a scalar or another defined array that will be appended to the data already within array.

I am trying to create a function titled pop_array, but I am struggling with how to write it. Normally you could just iterate over a for loop from indice to the assigned length, overwrite the first indice and move all others to the left. Unfortunately in this case array is a void variable, and I run into problems assigning data to a void. I have tried some implementations with memcp and memmove, but I can not find a solution where the compiler allows me to assign the data to a void type. Any thoughts or help would be appreciated. The code is shown below.

array.h

 #ifndef ARRAY_H
#define ARRAY_H

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

typedef struct
{
    void *array;  // Pointer to array
    size_t len;   // Active length of array
    size_t size;  // Number of allocated indizes
    int elem;     // Memory consumption per indice
    char *name;   // The array name
    char *dtype;  // A string representing the datatype
} Array;
void array_mem_alloc(Array *array, size_t num_indices);
Array init_array(char *dtype, size_t num_indices, char *name);
int append_array(Array *array, void *elements, size_t count);
int pop_array(Array *array, int indice);

Array.c

void array_mem_alloc(Array *array, size_t num_indices) {
    // Determine the total memory allocation and assign to pointer
    void *pointer;
    pointer = malloc(num_indices * array->elem);

    // If memory is full fail gracefully
    if (pointer == NULL) {
        printf("Unable to allocate memory, exiting.\n");
        free(pointer);
        exit(0);
    }
    // Allocate resources and instantiate Array
    else {
        array->array = pointer;
        array->len = 0;
        array->size = num_indices;
    }
}
// --------------------------------------------------------------------------------

Array init_array(char *dtype, size_t num_indices, char *name) {
    // Determine memory blocks based on data type
    int size;
    if (strcmp(dtype, "float") == 0) size = sizeof(float);
    else if (strcmp(dtype, "int") == 0) size = sizeof(int);
    else if (strcmp(dtype, "double") == 0) size = sizeof(double);
    else if (strcmp(dtype, "char") == 0) size = sizeof(char);
    else {
        printf("Data type not correctly entered into init_array, exiting program!\n");
        exit(0);
    }

    // Allocate indice size and call array_mem_alloc
    Array array;
    array.dtype = dtype;
    array.elem = size;
    array_mem_alloc(&array, num_indices);
    array.name = name;
    return array;
}
// --------------------------------------------------------------------------------

int append_array(Array *array, void *elements, size_t count) {
    // Allocae more memory if necessary
    if (array->len   count > array->size) {
        size_t size = (array->len   count) * 2;
        void *pointer = realloc(array->array, size * array->elem);
        // If memory is full return operations
        if (pointer == NULL) {
            printf("Unable to allocate memory, exiting.\n");
            return 0;
        }
        // Allocate memory to variables and increment array size
        array->array = pointer;
        array->size = size;
    }
    // Append variables and increment the array length
    memcpy((char *)array->array   array->len * array->elem, elements, count * array->elem);
    array->len  = count;
    return 1;
}
// --------------------------------------------------------------------------------

int pop_array(Array *array, int indice) {
    if (indice >= array->len) {
        printf("Indice %d out of bounds for pop_array", indice);
        return 0;
    }
    
    for (int i = index; i < array->len; i  ) {
        // This does not work because I cannot assign to a void type
        // - I have tried to several solutions with append_array and memcpy
        //   but all solutions seem to run into a problem where assigning data
        //   already in the array is not possible.
        array->array[i] = array->array[i 1];
    }
    // Decrement array length
    array->len -= 1;
    return 1;
}

CodePudding user response:

Belaying potential alignment issues, you don't need a loop. I believe this is all you need to shift the right-most desired elements 'back' one 'slot':

unsigned char *dst = (unsigned char*)array->array   indice * array->elem;
memmove(dst, dst   array->elem, array->elem * (array->len - indice - 1));

CodePudding user response:

There are lot of things that go wrong in your implementation.

For example, the way you use your name char pointer can lead to it pointing to deallocated memory. The following code :

void foo(Array *array) {
    char name[] = "abc";
    char type[] = "int";
    init_array(type, name, array);
}

int main(int args, char **argv) {
    Array array;
    foo(&array);
    puts(array.name);
}

leads to undefined behavior, because array.name and array.dtype point to chunks of memory which have been popped from the stack.

One can also mention that you only manage very few cases. What happens if the user input "short" as dtype ?

For your specific problem, memmove is your friend :

void* slot = array->array   array->elem * indice;
memcpy(array->array   array->len * array->elem, slot, array->elem);
--vector->len;
memmove(slot, (const void *) slot   array->elem, (array->len - indice) * array->elem);
  • Related