Send 2darray through network - c

Say I want to send a 2 dimensional array through network:
int array_to_send[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
I save this array in a generic one because I don't know the type at compile time:
struct generic_array_type {
/* Pointer to array */
void *array;
/* Size of one element */
size_t elem_size;
/* Number of dimensions, max 3 */
size_t num_dimensions;
/* Dimensions */
size_t dimensions[3];
};
typedef struct generic_array_type genarray_t;
Then I fill the struct like this:
genarray_t _2dgarray;
/* Fill in data */
_2dgarray.array = array_to_send;
_2dgarray.elem_size = sizeof(int);
_2dgarray.num_dimensions = 2;
_2dgarray.dimensions[0] = 2;
_2dgarray.dimensions[1] = 4;
Now my question is: since int are affected by the endianess of the system I want to convert each element to network order. I do it like this:
/* Now, compute the size of the array in bytes */
// Simplified for this example
size_t garray_size = _2dgarray.dimensions[0] * _2dgarray.dimensions[1] * _2dgarray.elem_size;
/* Go through each element */
{
unsigned char *ptr = _2dgarray.array;
void (*swapBytes)(void *) = NULL;
switch (_2dgarray.elem_size) {
case 2:
swapBytes = swapBytes2;
break;
case 4:
swapBytes = swapBytes4;
break;
default:
fprintf(stderr, "Byte swap not implemented for %zu\n", _2dgarray.elem_size);
break;
}
for (size_t i = 0; i < garray_size; i += _2dgarray.elem_size) {
/* Pointer to element */
void *element = ptr + i;
/* Swap bytes */
swapBytes(element);
}
}
/* Now the array should have network order endianess, convert it to char buffer */
char buffer[garray_size];
memcpy(buffer, (char *)_2dgarray.array, garray_size);
/* Send "buffer" over network... */
Function for byteswapping:
void swapBytes2(void *ptr)
{
uint16_t val = *(uint16_t *)ptr;
*(uint16_t *)ptr = _bswap16(val);
}
void swapBytes4(void *ptr)
{
uint32_t val = *(uint32_t *)ptr;
*(uint32_t *)ptr = _bswap32(val);
}
It works, but I'm really unsure if this is the right way to do it. Especially the "memcpy" part which converts the array to a char array.

Related

allocate a 3d array that contains strings

I wrote a 'class' that can store strings (2d array/matrix). This works fine, but now I need a way to store that matrix. So I need to calloc the 3tensor but i am not sure how to do it beyond the matrice allocation.
My 2d array looks like this: \
#pragma once
#include <stdlib.h>
#include <stdint.h> /* uints */
#include <string.h> /* char */
/* struct that holds data about the matrix, so it can be resized when needed */
struct Arr {
char** cArr;
unsigned int currentSize, maxSize;
};
/* initial allocation function that is used when a array needs te be calloced at first */
uint8_t initialAlloc(struct Arr* arr);
/* reallocate the array to a hardcoded size */
uint8_t reallocArr(struct Arr* arr);
/* add an element to the array in arr */
void addToArr(struct Arr* arr, char* element);
#include "arr.h"
uint8_t initialAlloc(struct Arr* arr) {
arr->currentSize = 0;
arr->maxSize = 2;
arr->cArr = calloc(2, sizeof(char*));
if (!arr->cArr) {
return 0;
}
for (uint8_t x = 0; x < arr->maxSize; ++x) {
arr->cArr[x] = calloc(600, sizeof(char));
if (!arr->cArr[x]) {
return 0;
}
}
return 1;
}
void addToArr(struct Arr* arr, char* element) {
if (arr->currentSize == arr->maxSize) {
if (!reallocArr(arr)) {
/* if the memory allocation fails something is bad wrong */
exit(1);
}
}
strncpy(arr->cArr[arr->currentSize], element, strlen(element));
++arr->currentSize;
}
uint8_t reallocArr(struct Arr* arr) {
uint16_t resizeTo = arr->maxSize * 2;
arr->cArr = realloc(arr->cArr, resizeTo * sizeof(char*));
if (!arr->cArr) {
return 0;
}
for (uint16_t x = arr->currentSize; x < resizeTo; ++x) {
arr->cArr[x] = calloc(600, sizeof(char));
if (!arr->cArr[x]) {
return 0;
}
}
arr->maxSize = arr->maxSize * 2;
return 1;
}
I want to use the Arr struct inside the 3tensor.
#pragma once
#include <stdlib.h> /* basic functions */
#include <stdint.h> /* uint */
/* user includes */
#include "characterArr.h"
/* user includes */
struct Tensor {
char*** mArr;
unsigned int currentSize, maxSize;
};
uint8_t initialAllocTensor(struct Tensor* tensor);
uint8_t reallocTensor(struct Tensor* tensor);
void addToTensor(struct Tensor* tensor, struct Arr* arr);
/* user includes */
#include "tensor.h"
/* user includes */
/* initial allocation function that is used when a array needs te be calloced at first */
uint8_t initialAllocTensor(struct Tensor* tensor) {
tensor->currentSize = 0;
tensor->maxSize = 2;
tensor->mArr = calloc(2, sizeof(struct *Arr));
if (!tensor->mArr) {
return 0;
}
for (uint8_t x = 0; x < arr->maxSize; ++x) {
arr->cArr[x] = calloc(600, sizeof(char));
if (!arr->cArr[x]) {
return 0;
}
}
return 1;
}
After I allocate the memory that will contain the pointers to the Arr struct, I get lost. The reason is, I need the information in the Arr struct for later use, so why would I recopy all the data from the Arr struct that i already have into the newly created 3Tensor. Could I just store a pointer to the Arr struct in the Tensor then be done? If i do this, would I be able to access the Arr struct just fine? Since I malloced this, I wouldn't need to worry about the memory getting taken when it goes out of scope.

How to get the i-th element of vector

I want to get the i-th element of the void*. I understand that it is void type and I have to give it a certain data type. The idea behind this is that it should be working for different data types. If i am right about the issue, how do I implement that?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 64
typedef struct Vector {
void *data;
size_t element_size;
size_t size;
size_t capacity;
} Vector;
// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void init_vector(Vector *vector, size_t block_size, size_t element_size){
vector->data = (void*) malloc(block_size * element_size); // i am questioning whether this is correct
vector->element_size = element_size;
vector->size = 0;
vector->capacity = block_size * element_size;
}
void resize(Vector *vector, size_t new_size){
void *data2 = (void*) malloc(new_size * vector->element_size);
int i=0;
memmove(data2,vector->data,new_size * vector->element_size);
vector->data = data2;
if(new_size > vector->size){
for(i=vector->size-1;i<new_size;i++){
vector->data[i]=0; // here is the problem
}
}else{
vector->size = new_size;
}
vector->capacity = new_size*vector->element_size;
}
C is a language for adrenaline junkies. It's always akin to free climbing, sky diving and formula 1 racing.
Defensive programming is not a perfect protection against all kinds of "sabotage" by a caller, but it is better than nothing.
The code below is in that very spirit. It cannot detect all possible things going wrong (like corrupted memory or if the data pointer in the vector is just a random value), but it showcases the least amount of defensive programming one should use if writing in that language.
C has "pointer arithmetic" as a feature. So, if you have e.g. a uint16_t * p = 1000; and you access p + 1, it is accessing 1002 (i.e. p + sizeof(uint16_t) * 1).
And this is the trick, how you can access an element in such a very weakly typed vector. You cast the void pointer to a byte pointer (as an example) and then use pointer arithmetic.
void* at(Vector* v, size_t index) {
// C needs manual sanity checks for the preconditions
if (NULL == v) return NULL;
if (v->size <= index) return NULL;
if (NULL == v->data) return NULL;
// now we can be (reasonably, as much as we can tell) sure, we did not get garbage as arguments...
return ((char*)(v->data)) + index * v->element_size;
}
Your vector functions can not [meaningfully] access the array data using the index or pointer syntax such as:
vector->data[i]=0;
That is because it's a void * pointer but, more importantly, if element_size is (e.g.) 8, does that mean vector->data points to a double or unsigned long long?
Only the caller of your functions can do this:
Vector *vec = calloc(1,sizeof(*vec));
init_vector(vec,100,sizeof(double));
double *ptr = vec->data;
for (size_t idx = 0; idx < vec->size; ++idx)
ptr[idx] = idx;
When you extend the array in resize, you can only mem* functions.
Replace your for loop with:
memset(&vector->data[vector->size * vector->element_size],0,
(new_size - vector->size) * vector->element_size);
UPDATE:
There are some more issues. Although you can have capacity be a byte count, it is more usual for it to be an element count (just like size).
When I create such dynamic array/vector objects/functions, I usually do not have resize do initialization of the elements.
That's because it doesn't [really] know how to initialize the elements. For a c++ vector, the constructor knows.
So, if we wish resize [and init_vector] to do this, we need to provide a function pointer for this.
Here's some refactored code to illustrate:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 64
typedef struct Vector Vector;
typedef void (*vecinit_p)(Vector *,size_t idx,size_t count);
struct Vector {
void *data; // pointer to array data
size_t element_size; // number of bytes in an array element
size_t capacity; // number of elements allocated
size_t size; // number of elements in use
vecinit_p initfnc; // pointer to init function
};
// vecptr -- get pointer to i'th element
void *
vecptr(Vector *vec,size_t idx)
// idx -- index of desired element
{
void *ptr;
idx *= vec->element_size;
ptr = vec->data;
ptr += idx;
return ptr;
}
// init_data -- initialize data elements
void
init_data(Vector *vec,size_t idx,size_t count)
// idx -- starting index
// count -- number of elements to initialize
{
void *ptr = vecptr(vec,idx + 0);
void *end = vecptr(vec,idx + count);
memset(ptr,0,end - ptr);
}
// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void
init_vector(Vector *vec, size_t block_size, size_t element_size,vecinit_p fnc)
// block_size -- number of elements
// element_size -- number of bytes in a single element
{
size_t new_len = block_size * element_size;
vec->data = calloc(1,new_len);
vec->element_size = element_size;
vec->size = 0;
vec->capacity = block_size;
// provide a "default" constructor
if (fnc == NULL)
fnc = init_data;
vec->initfnc = fnc;
fnc(vec,0,vec->capacity);
}
// resize -- resize the array
void
resize(Vector *vec, size_t new_cap)
// new_cap -- desired new capacity
{
// get byte length
size_t new_len = new_cap * vec->element_size;
void *data2 = malloc(new_len);
if (data2 == NULL) {
perror("malloc");
exit(1);
}
vec->data = data2;
// get old capacity and set new capacity
size_t old_cap = vec->capacity;
vec->capacity = new_cap;
// initialize new elements
if (new_cap > old_cap)
vec->initfnc(vec,old_cap,old_cap - new_cap);
}
// vecpush -- append element to array
// RETURNS: pointer to "pushed" element
void *
vecpush(Vector *vec)
{
// increase array capacity if needed
if (vec->size >= vec->capacity)
resize(vec,vec->capacity + 10);
// point to element
void *ptr = vecptr(vec,vec->size++);
return ptr;
}

How to properly make a dynamically allocated multi-array in C

I have been working to create a set of functions that allow for the creation and manipulation of a dynamically allocated array in C for any data type. I have created several functions, but of most relevance to this post are the following functions;
vector_mem_alloc This function is not called directly, but when indirectly called in a wrapper it allocates memory for the array based on the data type
init_vector This function is called directly by a user and is a wrapper around vector_mem_alloc. This function preps data and instantiates certain parameters in the Vector struct.
append_vector This function allows a user to append the 1-D array with scalar values or another already created array.
The code for these working functions is shown below.
vector.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.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;
} Vector;
// --------------------------------------------------------------------------------
void vector_mem_alloc(Vector *array, size_t num_indices);
// --------------------------------------------------------------------------------
Vector init_vector(dat_type dat, size_t num_indices, char *name);
// --------------------------------------------------------------------------------
int append_vector(Vector *array, void *elements, size_t count);
#endif /* ARRAY_H */
vector.c
#include "vector.h"
// Begin code
void vector_mem_alloc(Vector *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;
}
}
// --------------------------------------------------------------------------------
Vector init_vector(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
Vector array;
array.dat = dat;
array.elem = size;
vector_mem_alloc(&array, num_indices);
strncpy(array.name, name, sizeof(array.name));
return array;
}
// --------------------------------------------------------------------------------
int append_vector(Vector *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;
}
main.c
// - This shows an implementation for an integer array, but it works
// for FLOAT, DOUBLE, and CHAR as well.
size_t indices = 10;
char name[6] = "array";
dat_type dtype = INT;
Vector arr_test = init_vector(dtype, indices, name);
int a[3] = {10, 9, 8};
append_vector(&arr_test, &a, 3);
Everything listed above works just fine. However, I am trying to expand the capability of the above code to cover multi-arrays of any data type; however, in the near term I am particularly interested in string arrays. I am trying to add another struct to the vector.h file that references marray[] as a data type of Vector. In the long run, I hope to be able to reference the name of each array in the multi-array and treat it similar to a dictionary. I have tried several function that might allow an interface like shown below, but so far none of them work. Does anyone have any suggestions that I might be able to use as a starting point in building this functionality. My intended addition to the vector.h file is shown below with a main.c implentation that shows how I hope to interface with this.
typedef struct
{
size_t len;
size_t size;
int elem;
dat_type dat;
Vector *marray[];
} MVector;
size_t indices = 10;
dat_type dtype = INT;
MVector arr_test = init_vector(dtype, indices);
int a[3] = {10, 9, 8};
append_vector(&arr_test[0], &a, 3);
int b = 3;
append_vector(&arr_test[1], &b, 1);
append_vector(&arr_test[1], &b, 1);
b = 4;
append_vector(&arr_test[1], &b, 1)
append_vector(&arr_test[1], &b, 1);
// result [[10, 9, 8], [3, 3, 4, 4]]

How to pop a dynamically allocated array in C

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;
}
main.c
int main(int argc, const char * argv[]) {
size_t indices = 10;
char name[6] = "array";
char dtype[4] = "int";
Array arr_test = init_array(dtype, indices, name);
int a[3] = {10, 9, 8};
append_array(&arr_test, &a, 3);
// pop array function call here
}
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));
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);

Assign variable void* to array of void C

So I have an array, without any specified type:
void* buff = malloc(size*eltSize);
And I have a function, that has a void* parameter, and I want to assign it to the array, something like this:
void function(void* p1){
buff[i] = p1;
}
I know that this doesn't work, but say I want to make it as generic as possible, what's the best way to do? Remember, I have no idea about the types used (It should accept any type possible; even struct).
Thank you
You have to pass the element size (and the array index, for that matter) manually each time, similar to how qsort works. You'd have to change your function to something like:
void function(void * buff, void * p1, size_t elt_size, size_t index){
memcpy(((char *) buff) + index * elt_size, p1, elt_size);
}
and call it such as:
int array[] = {3, 1, 4, 1, 5, 9};
int n = 8;
function(array, &n, sizeof(n), 5); // Equivalent to array[5] = n;
A full working example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void function(void * buf, void * data, size_t elt_size, size_t index)
{
memcpy(((char *) buf) + index * elt_size, data, elt_size);
}
int main(void)
{
int narray[] = {3, 1, 4, 1, 5, 9};
int n = 8;
function(narray, &n, sizeof(n), 5); // Equivalent to array[5] = n
for ( size_t i = 0; i < sizeof(narray) / sizeof(narray[0]); ++i ) {
printf("Value of element [%zu] is: %d\n", i, narray[i]);
}
char * sarray[] = {"The", "mome", "raths", "outgrabe"};
char * p = "barked";
function(sarray, &p, sizeof(p), 3); // Equivalent to sarray[3] = p
for ( size_t i = 0; i < sizeof(sarray) / sizeof(sarray[0]); ++i ) {
printf("Value of element [%zu] is: %s\n", i, sarray[i]);
}
return 0;
}
with output:
Paul#Pauls-iMac:~/Documents/src/sandbox$ ./generic2
Value of element [0] is: 3
Value of element [1] is: 1
Value of element [2] is: 4
Value of element [3] is: 1
Value of element [4] is: 5
Value of element [5] is: 8
Value of element [0] is: The
Value of element [1] is: mome
Value of element [2] is: raths
Value of element [3] is: barked
Paul#Pauls-iMac:~/Documents/src/sandbox$
Obviously it will work just as well with arrays dynamically allocated with malloc() as it will with the regular arrays that this example uses.
You can eliminate the need to pass the element size every time if you create a struct to hold the data and the element size together, for instance:
struct generic_array {
void * data;
size_t elt_size;
}
When you pass a pointer to this struct to your function, it'll be able to access the element size itself, both eliminating the need for you to provide it, and eliminating a whole category of bugs arising from you inadvertently passing the wrong size. If you add a third member to store the number of elements you initially malloc()ed, then you can do bounds-checking, too.
Full working example of that approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct generic_array {
void * data;
size_t elt_size;
size_t size;
};
struct generic_array * generic_array_create(const size_t elt_size,
const size_t size)
{
struct generic_array * new_array = malloc(sizeof *new_array);
if ( !new_array ) {
perror("couldn't allocate memory for array");
exit(EXIT_FAILURE);
}
void * data = malloc(size * elt_size);
if ( !data ) {
perror("couldn't allocate memory for array data");
exit(EXIT_FAILURE);
}
new_array->data = data;
new_array->elt_size = elt_size;
new_array->size = size;
return new_array;
}
void generic_array_destroy(struct generic_array * array)
{
free(array->data);
free(array);
}
void generic_array_set(struct generic_array * array, void * elem,
const size_t index)
{
if ( index >= array->size ) {
fprintf(stderr, "Index %zu out of bounds of size %zu.\n",
index, array->size);
exit(EXIT_FAILURE);
}
memcpy(((char *)array->data) + index * array->elt_size,
elem, array->elt_size);
}
void generic_array_get(struct generic_array * array, void * elem,
const size_t index)
{
if ( index >= array->size ) {
fprintf(stderr, "Index %zu out of bounds of size %zu.\n",
index, array->size);
exit(EXIT_FAILURE);
}
memcpy(elem, ((char *)array->data) + index * array->elt_size,
array->elt_size);
}
int main(void)
{
int narray[] = {3, 1, 4, 1, 5, 9};
const size_t nsize = sizeof(narray) / sizeof(narray[0]);
struct generic_array * garray = generic_array_create(sizeof(int), nsize);
for ( size_t i = 0; i < nsize; ++i ) {
generic_array_set(garray, &narray[i], i);
}
for ( size_t i = 0; i < nsize; ++i ) {
int n;
generic_array_get(garray, &n, i);
printf("Value of element %zu: %d\n", i, n);
}
generic_array_destroy(garray);
return 0;
}
If you want to copy an object, and you don't know its type, only its size, you use memcpy:
void* buff = malloc(size*eltSize);
void function(void* p1) {
memcpy((char *)buff + i * eltSize, p1, eltSize);
}
Since you don't know the type, you can't use indexing directly, but rather have to manually calculate the address with pointer arithmetic.
Since you try to insert pointer to void type as element to buff, then buff must be of void** type.
int i = 0;
void* *buff = malloc(size * sizeof(void*));
if (buff == NULL)
// handle error
void function(void* p1) {
buff[i] = p1; // now OK
}
This is a potential solution if you want to remember the corresponding type of each data stored in your generic array. I used a fixed size array, and add basic type in the enum and just two exemple of how to get back your data in their respective type.
You could use function pointers if you have more type and don't want to use a lot the 'if' statements.
#include <stdio.h>
enum type {
INT,
FLOAT,
CHAR,
STRING
};
struct gen_array {
enum type elm_type;
void *data;
};
int to_int(void *data) {
return ((int) data);
}
char *to_string(void *data) {
return ((char *) data);
}
void printer(struct gen_array *arr, size_t size) {
for (size_t i = 0; i < size; i++) {
if (arr[i].elm_type == STRING)
printf("%s\n", to_string(arr[i].data));
if (arr[i].elm_type == INT)
printf("%d\n", to_int(arr[i].data));
}
}
int main(void) {
struct gen_array buff[2];
struct gen_array elm_0;
elm_0.elm_type = INT;
elm_0.data = (void*)10;
buff[0] = elm_0;
struct gen_array elm_1;
elm_1.elm_type = STRING;
elm_1.data = (void*)"helloWorld!";
buff[1] = elm_1;
printer(buff, 2);
return (0);
}

Resources