Creating Dynamic array as part of struct vs dynamic int array - arrays

I am working with dynamic arrays, consider two scenarios -
Scenario 1:
typedef struct
{
int *array;
int dataPtr;
} A;
static A dynaArray;
//Call the function as
dynaAdd(&dynaArray, <pointer to data block>, <length> );
static bool dynaAdd(A *data, int *dataCopy, int len)
{
int newlen = (data->dataPtr + len);
data->array = (int *) realloc(data->array, newlen * sizeof(int));
if (!data->array)
{
return FALSE;
}
memcpy(&data->array[data->dataPtr], dataCopy, len* sizeof(int));
data->dataPtr += len; // update data ptr
return TRUE;
}
I create a static struct A data (say) and pass to a function as pointer which reallocates length for A->array everytime and adds data to it, maintaining a dataPtr for index. This works perfectly fine where I can create an A->array of say length 100, use the data and free the pointer and null.
On the other scenario 2 -
static int *dynArray;
//Call the function as
dynaAdd(dynaArray, <pointer to data block>, <len>, <dataIdx> );
static bool dynaAdd(int *data, int *dataCopy, int len, int dataIdx)
{
int newlen = (dataIdx + len);
data = (int *) realloc(data, newlen * sizeof(int));
if (!data)
{
return FALSE;
}
memcpy(&data[dataIdx], dataCopy, len* sizeof(int));
dataIdx += len ;
return TRUE;
}
I just use int *array instead of struct, maintaining a static int dataPtr(say) to keep track of the next index, pass the array pointer to the function and dynamically grow the array and add contents to it. However, the program crashes after creating some x length array (which keeps varying).
Can someone help understand the difference between the two approaches ? In both scenarios, the goal is to create a dynamic array and keep adding contents to it until the certain data index and then free the array.
Thank you.

In the second example, you're modifying data which is local to the function. Such a change is not reflected in the calling function, so dynArray doesn't change. And since you reallocated memory, if the memory moved then this pointer is now invalid and attempting to dereference it triggers undefined behavior.
You need to change the function to accept the address of a int *, i.e. an int **, for the first argument and make the corresponding changes. You'll also want to make dataIdx a int * so changes to that are also propagated back
So your function would now look like this:
static bool dynaAdd(int **data, int *dataCopy, int len, int *dataIdx)
{
int newlen = (*dataIdx + len);
*data = realloc(*data, newlen * sizeof(int));
if (!*data)
{
return FALSE;
}
memcpy(&(*data)[*dataIdx], dataCopy, len* sizeof(int));
*dataIdx += len ;
return TRUE;
}

Related

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

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);
}
}
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.
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.

Why doesn't my function work to adjust my pointer's content, unless i return one to assign it

My question is what is the difference between these two? one is a void, and another one returns a 2d array, they however do the same but the functionality doesn't seem to do the same? i must be misunderstanding pointers here.
I thought pointers stored nothing but an adress to point to, so if i pass one as a parameter, and change the contents and to where it points, don't i do the same as re-assigning it to the functions return value.
on the end of both functions we print the first line, they did so on both. but whilst printing the adjusted grid by the void function in my Main i get a segfault.
char **ft_grid_gen(int size)
{
char **map;
int index;
int elem_index;
int sq_root;
index = 0;
elem_index = 0;
sq_root = ft_sqrt(size * 4);
map = (char**)malloc(sq_root * sizeof(char *));
while (index < sq_root)
{
map[index] = (char*)malloc(sq_root * sizeof(char));
while (elem_index < sq_root)
{
map[index][elem_index] = '.';
elem_index++;
}
index++;
elem_index = 0;
}
printf("GENERATED NEW GRID of size %s!\n", map[0]);
return (map);
}
void ft_grid_gen(char **map, int size)
{
int index;
int elem_index;
int sq_root;
index = 0;
elem_index = 0;
sq_root = ft_sqrt(size * 4);
map = (char**)malloc(sq_root * sizeof(char *));
while (index < sq_root)
{
map[index] = (char*)malloc(sq_root * sizeof(char));
while (elem_index < sq_root)
{
map[index][elem_index] = '.';
elem_index++;
}
index++;
elem_index = 0;
}
printf("GENERATED NEW GRID of size %s!\n", map[0]);
}
The difference is that the first function returns something you can use later on. In the second function, you pass in a char** by value, and then with:
map = (char**)malloc(sq_root * sizeof(char *));
You assign a new value to the local variable map, which was assigned its first value through the function parameter. However this does not affect the original char** variable in main() -- because it was passed in by value like everything else in C. If you wanted to change the main() variable, you would have passed a pointer to it (i.e. char***) and de-referenced it in this function, like:
*map = (char**)malloc(sq_root * sizeof(char *));
Note that ft_grid_gen don't just change that to which map points; it also change map itself.
Changing map has no more effect on the caller than changing size. Whatever variable you used as a parameter in the caller still point to "nowhere useful" after ft_grid_gen.
If you want to return a value via a parameter, you need to pass a pointer to the variable that will receive the value.
void f(int *i_ptr) {
*i_ptr = 123;
}
int i;
f(&i);
In your case, this would be
void ft_grid_gen(char ***map_ptr, int size) {
...
*map_ptr = map;
}
char **map;
f(&map);
Alternatively, you could allocate the memory on the outside. Since the function no longer has a need to change map, the value of map can be passed directly.
void ft_grid_gen(char **map, int size) {
...
}
char **map = malloc(ft_sqrt(size * 4) * sizeof(char*));
ft_grid_gen(map, size);
(Obviously, this approach is far from ideal in this specific situation.)

Using malloc on variables created from typedef in a function

I want to create a new intarr_t with initial size len, but I've never handled this type of problem with a typedef'ed variable.
My problem is that intarr_create() should allocate the array space and then return a pointer to it if malloc was successful or a pointer to NULL if I failed. How can I fix this?
Also, why there is a * symbol in the function?
Here's my code:
#include <stdio.h>
typedef struct {
int* data;
unsigned int len;
} intarr_t;
intarr_t* intarr_create(unsigned int len) {
//intarr_t with initial size len
intarr_t = (int *) malloc(len); // not working here, can someone explain why?
if(intarr_t != NULL) {
return intarr_t;
} else {
return NULL;
}
}
int main() {
int len = 15;
int h = intarr_create(len);
printf("%d\n", h);
return 0;
}
It's not working because you did not give your variable a name. Also, int* and intarr_t are not the same type, so you will get a type mismatch unless you change the cast.
Rewrite your function into this:
intarr_t* intarr_create(unsigned int len)
{
intarr_t *result;
result = (intarr_t *)malloc(sizeof(intarr_t)); // allocate memory for struct
if(result != NULL)
{
result->data = (int *)malloc(len * sizeof(int)); // allocate memory for data
result->len = len;
if (result->data == NULL)
{
/* handle error */
}
}
else
{
/* handle error */
}
return (result);
}
You have to do a "double" malloc to get it right. First you have to allocate the memory for the intarr_t and if that was successful you have to allocate the memory for the data array.
Additionally malloc returns a void * which must be cast to the correct pointer type (should be a warning or maybe even an error with some compilers).
You have a few problems with your intarr_create function. First of all, you need to name your intarr_t variable. Now you have the slightly trickier problem of allocating memory for the actual array of integers in addition to your intarr structure. Remember, that you will have to call delete twice to destroy this object. Once on the data, and once on the actual structure itself.
intarr_t* intarr_create(unsigned int len)
{
intarr_t* array = (intarr_t*)malloc(sizeof(intarr_t));
array->data = (int*)malloc(len * sizeof(int));
return array;
}

Writing to Dynamic Memory

I have the following pointer to structure
struct ALIST
{
short sPeriod;
long lDate;
}*list_ptr;
list_ptr = malloc(sizeof(*list_ptr));
Now if I have a global variable sIndex which I initialize to zero, is it possible to do this?
(list_ptr + sIndex)->sPeriod = period_variable;
(list_ptr + sIndex)->lDate = date_variable;
sIndex++
Is there a more efficient method?
This looks like you want to allocate a dynamic array. Make a size variable and set it to your starting size for the array.
Something like:
size_t list_size = 10;
struct ALIST list_ptr = 0;
size_t i;
list_ptr = malloc(sizeof(*list_ptr) * list_size);
for(i=0; i<list_size; ++i) {
list_ptr[i].sPeriod = period;
list_ptr[i].lDate = date;
}
Now, if you don't know the size of the array then what you want ends up looking a lot like a C++ std::vector.
I'd build a C version that wraps the necessary information in a struct. Use realloc to resize it.
It might look like (NOTE THAT THIS IS COMPLETELY UNTESTED):
struct dynamic_ALIST {
struct ALIST *array;
size_t size;
size_t capacity;
};
void dynamic_ALIST_construct(struct dynamic_ALIST *x, size_t initial_size)
{
x->array = 0;
x->size = 0;
x->capacity = 0;
dynamic_ALIST_reserve(x, initial_size);
}
void dynamic_ALIST_reserve(struct dynamic_ALIST *x, size_t size)
{
struct ALIST *tmp = realloc(x->array, sizeof(*tmp) * size);
if(!tmp) abort();
x->array = tmp;
x->capacity = size;
}
struct ALIST* dynamic_ALIST_get(struct dynamic_ALIST *x, size_t offset)
{
if(offset < x->size) {
return x->array + offset;
}
if(offset < x->capacity) {
x->size = offset + 1;
return x->array + offset;
}
dynamic_ALIST_reserve(x, offset+1);
return dynamic_ALIST_get(x, offset);
}
Then you could use it like:
void f()
{
size_t item_index = 0;
struct dynamic_ALIST list;
FILE *fp = fopen("filename");
dynamic_ALIST_construct(list, 0);
while( read_item(fp, dynamic_ALIST_get(list,item_index)) ) {
item_index++;
}
fclose(fp);
}
You can make all kinds of changes to that. The get function might return an error instead of automatically creating new entries. You might make another function that increases the size. You might want to have a function that sets all the values to zero before returning new memory.
If you have a lot of different structs to wrap up you can put ALL of the above dynamic_ALIST struct, and construct, reserve, get functions into a macro. If you do it right then you just say:
NEW_DYNAMIC_STRUCT(ALIST);
And the preprocessor spits out a whole new copy with all the names changed.
I'll answer point by point:
Do those pointer manipulations only if you know what you are doing.
Assuming sIndex to be an int, with sIndex=0;, it is no problem but if you increment sIndex, you don't have that space allocated to use becuase you have malloc'd just one block.
You need to first do your allocation appropriately if you need to access multiple such blocks then:
list_ptr = malloc(sizeof(struct ALIST)*N); //replace N with the number of blocks you want

Getting size of void* for creating a simple generic dynamically allocated array - C

I have created a simple dynamic array in C.
typedef struct varray_t
{
void **memory;
size_t allocated;
size_t used;
int index;
} varray;
void
varray_init(varray **array)
{
*array = (varray*) malloc (sizeof(varray));
(*array)->memory = NULL;
(*array)->allocated = 0;
(*array)->used = 0;
(*array)->index = -1;
}
void
varray_push(varray *array, void *data, size_t size)
{
if ((array->allocated - array->used) < size) {
array->memory = realloc(array->memory, array->allocated + size);
array->allocated = array->allocated + size;
}
array->used = array->used + size;
array->memory[++array->index] = data;
}
int
varray_length(varray *array)
{
return array->index + 1;
}
void
varray_clear(varray *array)
{
int i;
for(i = 0; i < varray_length(array); i++)
{
array->memory[i] = NULL;
}
array->used = 0;
array->index = -1;
}
void
varray_free(varray *array)
{
free(array->memory);
free(array);
}
void*
varray_get(varray *array, int index)
{
if (index < 0 || index > array->index)
return NULL;
return array->memory[index];
}
This is working fine. But to add an item into the array, caller has to pass in the size of the element getting added. I can't figure out another way to get the size from the passed in void*. I am wondering is there a better way to design varray_push(varray *array, void *data, size_t size) so that size can be infered?
Any help would be great
Edited code after the suggestions
My array will contain only pointer elements. I have modified the code according to Blastfurnace's suggestion. New code will use sizeof(void*) and resize memory by a constant propotion to get amortized constant time on inserts.
void
varray_push(varray *array, void *data)
{
size_t toallocate;
size_t size = sizeof(void*);
if ((array->allocated - array->used) < size) {
toallocate = array->allocated == 0 ? size : (array->allocated * 2);
array->memory = realloc(array->memory, toallocate);
array->allocated = array->allocated + toallocate;
}
array->memory[++array->index] = data;
array->used = array->used + size;
}
If the array is going to contain only one type at a time, then you can store the size of the type of the array in varray_init.
Also, my suggestion is that instead of allocating memory fresh for each element, you can allocate memory for constant size each time, i.e. first allocate memory for 16 elements and then when you find that array is full when pushing an element realloc for 16 + 16 = 32 elements. In this way, you can avoid calling malloc again and again and also it is not good idea to keep mallocing for small size data seperately.
EDIT:
After considering Blastfurnace comment, I feel that you should actually be doing a memcpy of the data rather than assignment if your intention is to store the data and not the pointer to the data.
I have a simple to use linked list implementation that you can use. I wouldn't say it is text book quality but it is easy: https://github.com/inorton/xrlist
#include <stdio.h>
#include "xrlist.h"
xr_list_t * mylist = xrlist_new();
xrlist_push(mylist, (void*) ptr1);
xrlist_push(mylist, (void*) ptr2);
You iterate like so:-
xr_list_item_t * iter = mylist->head;
while ( iter != NULL )
{
printf(" * item [0x%x] contains [%s]\n",
(unsigned int) iter->object, (char*) iter->object );
iter = iter->next;
}
You also have the usual remove/add/free functions too. See more examples here.
If you want random access data like a dictionary you can also try out https://github.com/inorton/xrhash

Resources