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.