Home > Blockchain >  What is the most efficient way to replace an element in an array in C
What is the most efficient way to replace an element in an array in C

Time:04-12

I have been slowly working on a library that would allow a user to dynamically build an array in the C language. Of the many functions I have written, I have developed a solution to replace the value at a user defined index with another value, but I am not sure it is a very efficient solution and would like any thoughts if someone might have a better method that does nto require creating several intermediate arrays to store data.

I am using a typedef struct titled Array which acts as a container for the pointer variable array, as well as len that contains the active length of the array, size which contains the allocated size of the array, elem which contains the memory allocation per indice, which can change depending on the data type chosen by the user, name which is a character string the user can assign to each array instantiation, and dat_type which references an enum to describe the data type the array was instantiated with.

So far I have built several functions to work in conjunction with the Array container, of which the most relevant for this question are the following functions;

type_dat An enum with variables for float, double, char, and int.

Array a struct that acts as a container for an array.

array_mem_alloc The function allocates memory for the array and should not be called directly.

init_array This function acts as a wrapper around array_mem_alloc and is how the user should instantiate an array. This returns an Array container.

append_array This allows a user to append a scalar or an array to the array within the Array container.

preappend_array This function allows a user to ad a scalar or an array to the beginning of an array within the Array container.

int_array_val This function retrieves integer variables from the array. There is a version of this function for each data type; however, this specific function is relevant to the example below.

replace_int_array_indice This function allows a user to enter a specific indices and replace the variable at that indice with another integer value. I have coded a solution for this function, but I do not believe it is an efficient implementation. In the below example, the implementation requires that two intermediate arrays be created to store data before the memmove and memcpy function can bring all of the data together. I am hoping that someone can tell me if there is a better solution for this function then the one I am providing below.

My files are shown below

array.h

#ifndef ARRAY_H
#define ARRAY_H

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

typedef enum
{
    FLOAT,
    DOUBLE,
    CHAR,
    INT
} dat_type;
// --------------------------------------------------------------------------------

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[20];   // The array name
    dat_type dat;
} Array;
// --------------------------------------------------------------------------------

void array_mem_alloc(Array *array, size_t num_indices);
// --------------------------------------------------------------------------------

Array init_array(dat_type dat, size_t num_indices, char *name);
// --------------------------------------------------------------------------------

int append_array(Array *array, void *elements, size_t count);
// --------------------------------------------------------------------------------

int int_array_val(Array *array, int indice);
// --------------------------------------------------------------------------------

int preappend_array(Array *array, void *elements, size_t count);
// --------------------------------------------------------------------------------

void replace_int_array_indice(Array *array, int index, int replacement_value);
#endif /* ARRAY_H */

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(dat_type dat, size_t num_indices, char *name) {
    // Determine memory blocks based on data type
    int size;
    switch(dat) {
        case FLOAT:
            size = sizeof(float);
            break;
        case INT:
            size = sizeof(int);
            break;
        case DOUBLE:
            size = sizeof(double);
            break;
        case CHAR:
            size = sizeof(char);
            break;
        default:
            printf("Data type not correctly entered, instantiating int array!\n");
            size = sizeof(int);
            dat = INT;
    }

    // Allocate indice size and call array_mem_alloc
    Array array;
    array.dat = dat;
    array.elem = size;
    array_mem_alloc(&array, num_indices);
    strcpy(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 preappend_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");
            exit(0);
        }
        // Allocate memory to variables and increment array size
        array->array = pointer;
        array->size = size;
    }
    // Preappend variables and increment the array length
    memmove(
    ((char *) array->array)   count * array->elem,
    array->array,
    array->len * array->elem);

    memcpy(array->array, elements, count * array->elem);
    array->len  = count;
    return 1;
}

// --------------------------------------------------------------------------------

void replace_int_array_indice(Array *array, int index, int replacement_value) {

    // THIS FUNCTION WORKS, BUT I DO NOT THINK IT IS AN EFFICIENT IMPLEMENTATION
    // Copy data to intermediate array
    int arr[array->len];
    memcpy(arr, array->array, array->elem * array->len);
    memmove(((char *) array->array), arr   index   1, array->len * array->elem);
    array->len -= index   1;

    // preappend with replacement value
    preappend_array(array, &replacement_value, 1);

    // preappend with initial indices up to replaced index
    int new_arr[index - 1];
    memcpy(new_arr, arr, index * array-> elem);
    preappend_array(array, &new_arr, index);
}

main.c

#include "array.h"

int main(int argc, const char * argc[]) {
    size_t indices = 10;
    char name[6] = "array";
    dat_type dtype = INT;
    Array arr_test = init_array(dtype, indices, name);
    int a[6] = {1, 2, 3, 4, 5, 6};
    append_array(&arr_test, a, 6);
    replace_int_array_indice(&arr_test, 1, 5);
    for (int j = 0; j < array_test.len; j  ) {
        printf("%d\n", int_array_val(&arr_test, j);
    }
}

CodePudding user response:

You are definitely overthinking this one.

You only need to replace one value in the array, so find the offset, cast correctly, dereference and assign:

* (int *) ((char *) array->array   index * array->elem) = replacement_value;

Or, continue to work with memcpy when building base functionality, and use that to create further abstractions.

void array_set(Array *array, size_t index, void *data) {
    memcpy(
        (char *) array->array   index * array->elem,
        data,
        array->elem);
}

void int_array_set(Array *array, size_t index, int value)
{
    array_set(array, index, &value);
}

Might want to check that index is valid.

CodePudding user response:

btw, you have a memory corruption vulnerability in your code. in init_array you are using strcpy(array.name, array);, when name is only 20 bytes long. and name is user controled. you should use strncpy(array.name, array, sizeof(array.name); instead.

  • Related