Hello i'implementing a smart vector in c, and i'm having problems with the reallocation of the buffer.
this is the struct that contains the array and its infos:
struct _vector
{
item* vec;
size_t elements;
size_t size;
};
item is just a typedef that in this case happens to be int.
I made several function to manage the array, but the one that should resize it, gives me problems.
(Vector is also a typedef for struct _vector* by the way)
This is the function:
void insertVector(const Vector vec,const int pos,const item a)
{
if(vec->elements==vec->size)
{
item* temp=realloc(vec->vec,(vec->size*2)*sizeof(item));
if(temp==NULL)
{
puts("Error: space unavailable");
return;
}
//vec->vec=realloc(vec->vec,(vec->size*2)*sizeof(item));
vec->vec=temp;
vec->size*=2;
}
int size=vec->elements;
if(pos>=0&&pos<=size)
{
for(int i=size;i>pos;i--)
{
vec->vec[i]=vec->vec[i-1];
}
vec->vec[pos]=a;
vec->elements+=1;
printf("size is %lu\nelements are %lu\n",vec->size,vec->elements);
}
}
I just shift the contents to make space for the new element, and it works fine, the problem is when the array is reallocated.
when the number of valid elements is equal to the actual size of the array,
i do a realloc to double the actual size.
As soon as that if activates though the realloc makes the program crash with this error:incorrect checksum for freed object.
The problem is in the if, because it only crashes when the size and elements are equal, if i comment out that section, everything works
I don't know what could it be.
EDIT:
The functions that i used to create and the initialise the instance i'm working with are:
Vector newVector(void)
{
Vector new=malloc(sizeof(*new));
new->vec=NULL;
new->elements=0;
new->size=0;
return new;
}
and
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size*2;
}
Based of your comment
I created a new vector setting to zero every field, then i used this function:
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size*2;
}
I think you are treating the size and the number of elements incorrectly. The
initVector function just allocates memory for the vec->vec array, so
vec->elements should be 0, not size. And vec->size should be size, not
size*2. So the correct function should be
// remove the const, you are modifying the data vec is pointing to
int initVector(Vector vec, size_t size)
{
if(vec == NULL)
return 0;
vec->vec = calloc(size, sizeof *vec->vec);
if(vec->vec == NULL)
return 0;
vec->elements = 0;
vec->size = size;
return 1;
}
Now the insertVector would only allocate new space, when all allocated spaces
are used.
And I suggest you use memmove to copy the memory:
// again, remove the const here
int insertVector(Vector vec, const size_t pos, const item a)
{
if(vec == NULL)
return 0;
if(vec->elements==vec->size)
{
item* temp=realloc(vec->vec,(vec->size*2)*sizeof *temp);
if(temp==NULL)
{
fprintf(stderr, "Error: space unavailable\n");
return 0;
}
vec->vec=temp;
vec->size*=2;
}
// I use vec->elements as upper limit,
// otherwise you could have "holes" in the array and
// you wouldn't realize it.
if(pos < 0 || pos > vec->elements)
{
fprintf(stderr, "invalid position\n");
return 0;
}
memmove(vec->vec + pos + 1, vec->vec + pos, (vec->elements - pos) * sizeof *vec->vec);
vec->vec[pos] = a;
vec->elements += 1;
printf("size is %lu\nelements are %lu\n",vec->size,vec->elements);
return 1;
}
In your initVector function, you set the size incorrectly, to two times what you allocated with calloc. This memory then gets overwritten as you are adding new elements and this is the reason the free fails when you finally invoke realloc. Change initVector to:
void initVector(const Vector vec,const size_t size)
{
vec->vec=calloc(size,sizeof(item));
vec->elements=size;
vec->size=size;
}
Related
I am trying to get into C and as a training example, I decided to write a simple dynamically sized list. But I am facing a weird problem, where the code only works up to an initial list size of 4. Starting at List size 5, I get an error.
typedef struct {
int* data;
int alloc_size;
int length;
} List;
List create(int init_size) {
List out;
out.data = (int*) malloc(init_size * sizeof(int));
out.alloc_size = init_size;
out.length = 0;
return out;
}
void list_push(List* list, int elem) {
if (list->length == list->alloc_size) {
list->data = (int*) realloc(list->data, 2 * list->alloc_size);
list->alloc_size *= 2;
}
*(list->data + list->length) = elem;
list->length++;
}
int list_pop(List* list) {
list->length--;
return *(list->data + list->length);
}
int main() {
List list = create(5);
for (int i = 0; i < 100; i++) {
list_push(&list, i);
}
while (list.length > 0) {
printf("%d\n", list_pop(&list));
}
return 0;
}
Up to create(4), everything works as expected. But if the list is created with create(5) (i.e. an initial size of 5), I get the following error: malloc: Incorrect checksum for freed object 0x7f7ff5c01778: probably modified after being freed. Corrupt value: 0x700000006. I can't really wrap my head around what would cause this to only work up to specific initial sizes, as the list size is dynamically reallocated anyway.
There are a couple of problems with this line
list->data = (int*) realloc(list->data, 2 * list->alloc_size);
The most evident is that 2 * list->alloc_size should be multiplied by the size in bytes of each element (sizeof(int) or sizeof(*(list->data)) in this case).
The most subtle is that the return value of realloc (and of the previous malloc) is not checked, but unconditionally assigned to list->data. The problem is that, on failure, it returns NULL, while the passed pointer (list->data) is not invalidated and should be freed to avoid leaks.
change to reallocation statement
list->data = (int*) realloc(list->data,sizeof(int) * 2 * list->alloc_size);
Second time you are trying to re-allocate lesser bytes than you already allocated, that's the reason for this
As part of a personal project, I'm trying to create a dynamic array of 2-tuples that show a) the line in a program and b) the number of bytecode tokens associated with that line. I've implemented this as a struct of arrays:
typedef struct{
int count; // Number of elements
int capacity; // total capacity of arraylist
int* lines;
int* lineCount;
}
this is based on the example from the codebase, as such:
int count;
int capacity;
uint8_t* bytes;
My problem comes from re-allocation - I have several helper functions/macros for growing and re-allocating the array lists memory - here particularly the macro GROW_ARRAY and reallocate(), as described below. When I try and re-allocate lines, it works fine, but I get a segmentation fault and realloc(): invalid old size several times when I attempt to reallocate lineCount after it
I'm using the code base from Bob Nystrom's Crafting Interpreters, especially this first part here https://craftinginterpreters.com/chunks-of-bytecode.html#challenges. Most of the code comes from there, albeit tinkered with some of having added
Mostly, I've added a lot of checks and been running this with all the debug features in gcc I can find. Notably, realloc(): invalid old size has stop appearing as I've tinkered with the code some.
EDIT: Added main function that should reproduce behavior
int main() {
LineArray lines;
// Initialize to 0 / NULL
initLineArray(&lines);
updateLineArray(&lines, 0, 1);
}
// the structure I'm using
typedef struct {
int capacity;
int count;
int* lines;
int* lineCount;
} LineArray;
/* Note LineArray has already been initialized earlier with
capacity=0;
count=0;
lines=NULL;
lineCount=NULL;
*/
void updateLineArray(LineArray* array, int line, int count) {
// IF line in `lines` -- update it
int index = containsLine(array, line);
if (index != -1) { // IF Index is not Error Code
// I think I fixed a bug here?
array->lineCount[index] += count;
return;
}
//ELSE -- add line to end (naturally appends); then increment
else {
//Check to see if array would be overgrown
if (array->capacity < array->count + 1) {
//IF yes, regrow array
int old_capacity = array->capacity;
array->capacity = GROW_CAPACITY(old_capacity);
// Reallocate arrays.
array->lines = GROW_ARRAY(array->lines, int, old_capacity,
array->capacity);
array->lineCount = GROW_ARRAY(array->lineCount, int, old_capacity,
array->capacity);
}
// Properly update the lines
array->lines[array->count] = line;
array->lineCount[array->count] = count;
array->count++;
return;
}
}
//The memory management functions/macros I'm using here
#define GROW_CAPACITY(capacity) \
((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_ARRAY(previous, type, oldCount, count) \
(type*) reallocate(previous, sizeof(type) * (oldCount), \
sizeof(type) * (count))
void* reallocate(void* previous, size_t oldSize, size_t newSize) {
// If size is null, erase it and get null_pointer
if (newSize == 0) {
free(previous);
return NULL;
}
// reallocate the data into a new size
// is Oldsize is zero :: malloc(data, newSize)
return realloc(previous, newSize);
}
Really stuck on the following problem. I'm creating a dynamic array, but when I re-size (for which the logic is creating a new array, copying the values of the old array into the new array, and then deleting old array). I keep getting a memory error when trying to free the memory of old array. See below; I feel like it's something obvious but my eyes just can't see it right now.
Struct to hold the array:
struct DynArr
{
TYPE *data; /* pointer to the data array */
int size; /* Number of elements in the array */
int capacity; /* capacity ofthe array */
};
Function to create a new array:
DynArr *newDynArr(int cap)
{
assert(cap > 0);
DynArr *r = (DynArr *)malloc(sizeof(DynArr));
assert(r != 0);
initDynArr(r, cap);
return r;
}
Initializing the array:
void initDynArr(DynArr *v, int capacity)
{
assert(capacity > 0);
assert(v != 0);
v->data = (TYPE *)malloc(sizeof(TYPE) * capacity);
assert(v->data != 0);
v->size = 0;
v->capacity = capacity;
}
Function to resize the array:
void _dynArrSetCapacity(DynArr *v, int newCap)
{
struct DynArr *newData;
/*new array to hold new values*/
newData = newDynArr(newCap);
///*Intialize the new array*/
initDynArr(newData,newCap);
/*Copy values from old array into new array*/
for (int a = 0; a < v->size; a++)
{
addDynArr(newData, v->data[a]);
}
/*Free the old array, data and array, Cant get this to work*/
/*freeDynArr(v) */
/*Have v point to new array*/
v = newData;
}
And function to free the memory, which is throwing me the error:
void freeDynArr(DynArr *v)
{
if (v->data!= 0)
{
free(v->data); /* free the space on the heap */
v->data = 0; /* make it point to null */
}
v->size = 0;
v->capacity = 0;
}
Your _dynArrSetCapacity function sets up another DynArr block by allocating it again using malloc. The problem here is that you neither return the newData array nor keep the old array (v).
This causes a problem on the caller side. You could've checked your code by doing a while(TRUE) type of block, before exiting _dynArrSetCapacity, which should show that your code doesn't crash before returning.
You have two solutions:
i) Return newData to the caller: While calling _dynArrSetCapacity, your code should update the DynArr variable being used, something like the following:
DynArr *updatedData = _dynArrSetCapacity(newData, newCapacity);
i) Pass a double pointer to _dynArrSetCapacity: The other way is to allow _dynArrSetCapacity to automatically update the pointer to the old DynArr struct. This would require the caller to pass a pointer to that pointer (resulting a double pointer of course). This is called passing out parameters sometimes.
void _dynArrSetCapacity(DynArr** oldData, int newCapacity);
{
DynArr *orgBuffer;// Our new dynamic array to hold the original
// buffer of data
.... Code that will initialize the buffer, do something cool....
_dynArrSetCapacity(&orgBuffer, NEW_CAPACITY);
}
The error you did while coding is that at the end of _dynArrSetCapacity you wrote v = newData and thought that it will be updated for the caller. That is wrong totally. That is because v is copied in the stack and then passed to the callee, which means any changes to v will not affect the originally passed argument (in code).
You don't need to do the realloation yourself, you can use a function called realloc that help you resize an malloc'd array, so the implementation is simpler.
void _dynArrSetCapacity(DynArr *v, int newCap)
{
if(v==NULL||v->data==NULL){
return; // Return early if they are NULL
}
/* Resize the data to newcap*sizeof(TYPE) bytes */
TYPE* tmp=(TYPE*)realloc(v->data,newCap*sizeof(TYPE));
if(tmp!=NULL){
v->data=tmp;
}
}
I'm facing a problem that seems complicated to me so i'll be very grateful to who can help me.
I'm sort of trying to replicate the array of string behavior only with structures
so instead of having for example
char **mys={"hello","this is a long message"};
I'm trying to have an array with structures (each structure containing an array of variable length). I'm not quite sure if I exposed my problem very well so i hope the code is going to indicate what i'm trying to do:
my two structures :
typedef struct
{
int *iTab;
int iTail;
}strDynArr;
typedef struct
{
strDynArr **iTab;
int iTailStruct;
int iTail;
}strDynDynArr;
All the functions related to those two structures :
void initArray(strDynArr *ar)
{
ar->iTab=malloc(sizeof(int));
ar->iTail=1;
}
void pushArray(int iElement,strDynArr *ar)
{
ar->iTab[ar->iTail-1]=iElement;
realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}
void freeDynArray(strDynArr *ar)
{
free(*ar->iTab);
}
void initDynDynArray(strDynDynArr *ar)
{
ar->iTab=malloc(sizeof(int));
ar->iTail=1;
ar->iTailStruct=1;
}
//the problem
void pushDynDynArray(strDynArr *daElement,strDynDynArr *ar)
{
//ar->iTab[ar->iTail-1]=daElement;
ar->iTailStruct+=(daElement->iTail+1);
realloc(ar->iTab,(ar->iTailStruct)*sizeof(int));
ar->iTab[ar->iTailStruct-(daElement->iTail+1)]=daElement;
//realloc(ar->iTab,(ar->iTail++)*sizeof(int));
}
void freeDynDynDynArray(strDynDynArr *ar)
{
free(*ar->iTab);
}
And the one function where i'm stuck is pushDynDynArray so far i've attempted two things either just using the pointer that points on à structure strDynArr but i don't know how space is managed at all so i tryed to allocate the size of the all the structures contained in the array of the structure strDynDynArr.
So what i would like to know is how to allocate space for my array (iTab) for a structure of type strDynDynArr.
In other words what is the way for me to store several structures containing for example :
//content of structure 1
int *iTab={2,3,4};
int iTail=3;
//content of structure 2
int *iTab={5,8,9,10,54,8,2};
int iTail=7;
All help is welcome and thanks à lot for it !
For simplicity, lets call each array of "strings" a table (of arrays), and each "string" an array of items. In your case, the item type seems to be int:
typedef int item;
typedef struct {
size_t size; /* Number of items allocated for */
size_t used; /* Number of items in item[] */
item *item;
} array;
typedef struct {
size_t size; /* Number of array (pointers) allocated for */
size_t used; /* Number of arrays in array[] */
array *array;
} table;
It is common to define initializer macros and functions for such types:
#define ARRAY_INIT { 0, 0, NULL }
#define TABLE_INIT { 0, 0, NULL }
static inline void array_init(array *ref)
{
if (!ref) {
fprintf(stderr, "array_init(): NULL array!\n");
exit(EXIT_FAILURE);
}
ref->size = 0;
ref->used = 0;
ref->item = NULL;
}
static inline void table_init(table *ref)
{
if (!ref) {
fprintf(stderr, "table_init(): NULL table!\n");
exit(EXIT_FAILURE);
}
ref->size = 0;
ref->used = 0;
ref->array = NULL;
}
Also, free functions are obviously useful:
static inline void array_free(array *ref)
{
if (!ref) {
fprintf(stderr, "array_free(): NULL array!\n");
exit(EXIT_FAILURE);
}
free(ref->item); /* Note: free(NULL) is safe; it does nothing. */
ref->size = 0;
ref->used = 0;
ref->item = NULL;
}
static inline void table_free(table *ref)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_free(): NULL table!\n");
exit(EXIT_FAILURE);
}
i = ref->size;
while (i-->0)
array_free(ref->array + i); /* array_free(&(ref->array[i])); */
free(ref->array);
ref->size = 0;
ref->used = 0;
ref->array = NULL;
}
When freeing a table, we want to free all (possible) arrays individually, then the memory used by the pointers. Note that one could assume that only used arrays are in use; however, I wanted the above to be thorough, and free all size arrays allocated for.
Both the init and free functions above take a pointer to an array or table. They should never be NULL. (That is, the item or array member in the structure may well be NULL; it's just that you should never call e.g. array_init(NULL); or table_free(NULL).)
Let us implement a function to push and pop individual ints from a solitary array (that may or may not be part of a table):
void array_push(array *ref, const item value)
{
if (!ref) {
fprintf(stderr, "array_push(): NULL array!\n");
exit(EXIT_FAILURE);
}
/* Need to grow the array? */
if (ref->used >= ref->size) {
size_t size; /* Minimum is ref->used + 1 */
void *temp;
/* Dynamic growth policy. Pulled from a hat. */
if (ref->used < 64)
size = 64; /* Minimum 64 elements */
else
if (ref->used < 1048576)
size = (3*ref->used) / 2; /* Grow by 50% up to 1048576 */
else
size = (ref->used | 1048575) + 1048577; /* Grow to next multiple of 1048576 */
temp = realloc(ref->item, size * sizeof ref->item[0]);
if (!temp)
return -1; /* Out of memory */
ref->item = temp;
ref->size = size;
}
/* Append value to array. */
ref->item[ref->used++] = value;
}
and a corresponding pop operation:
item array_pop(array *ref)
{
if (!ref) {
fprintf(stderr, "array_pop(): NULL array!\n");
exit(EXIT_FAILURE);
}
if (ref->used < 1) {
fprintf(stderr, "array_pop(): Array is already empty; nothing to pop.\n");
exit(EXIT_FAILURE);
}
/* Since ref->used is the *number* of items in the array,
we want to decrement it first, to get the last item in array. */
return ref->item[--ref->used];
}
In your program, you can use an array for example thus:
array scores = ARRAY_INIT;
array_push(&scores, 5);
array_push(&scores, 2);
printf("%d\n", array_pop(&scores)); /* Will print 2 */
printf("%d\n", array_pop(&scores)); /* Will print 5 */
array_free(&scores);
The line array scores = ARRAY_INIT; both declares and initializes the array (to an empty array). You could also equivalently use array scores; array_init(&scores);.
The resize or growth policy in array_push() is roughly along the lines I'd personally recommend, although the actual numerical values are pulled from a hat, and you may wish to adjust them. The idea is that there is a minimum number of items allocated for (say, 64). For bigger arrays, we increase the size fractionally, so that when the array is large, the size increase is also large. Many people use 100% increase in size (doubling the size, i.e. size = 2 * ref->used;), but I like 50% increase better (multiplying the size by one and one half, size = (3 * ref->used) / 2;). For huge arrays, we don't want to waste potentially lots of memory, so we allocate in fixed-size (but huge) chunks instead. (There is no need or real benefit to align the huge size to some multiple like I did; I just like it that way. And the more complicated code ensures you need to understand it and edit it, rather than submitting it raw as yours; otherwise, you'll instructor will catch you cheating.)
Pushing a single value to the last array in the table is now simple to implement:
void table_push_value(table *ref, const item value)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_push_value(): NULL table!\n");
exit(EXIT_FAILURE);
}
/* Ensure there is at least one array. */
if (!ref->size < 1) {
/* Empty table: ref->size = 0, and ref->used must be 0 too. */
const size_t size = 1; /* Allocate for exactly one array. */
void *temp;
temp = realloc(ref->array, size * sizeof ref->array[0]);
if (!temp) {
fprintf(stderr, "table_push_value(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ref->size = size;
ref->array = temp;
for (i = 0; i < size; i++)
array_init(ref->array + i); /* array_init(&(ref->array[i])); */
}
if (ref->used > 0)
i = ref->used - 1; /* Last array in table */
else
i = 0; /* Table is empty, so use first array */
array_push(ref->array + i, value); /* array_push(&(ref->array[i])); */
}
This time, you need special logic for an empty table, for both allocating the description for an array, as well as where to push.
Pop is simpler:
item table_pop_value(table *ref)
{
size_t i;
if (!ref) {
fprintf(stderr, "table_pop_value(): NULL table!\n");
exit(EXIT_FAILURE);
}
i = ref->used;
/* Find the last array with items in it, and pop from it. */
while (i-- > 0)
if (ref->array[i].used > 0) {
return array_pop(ref->array + i); /* array_pop(&(ref->array[i])); */
fprintf(stderr, "table_pop_value(): Empty table, no items to pop!\n");
exit(EXIT_FAILURE);
}
To push an entire array to a table (pushing the array of items in it, not making a copy of the items) is pretty simple, but we do need to implement a reallocation/growth policy again:
void table_push_array(table *ref, array *one)
{
if (!ref) {
fprintf(stderr, "table_push_array(): NULL table!\n");
exit(EXIT_FAILURE);
}
if (!one) {
fprintf(stderr, "table_push_array(): NULL array!\n");
exit(EXIT_FAILURE);
}
if (ref->used >= ref->size) {
size_t size, i;
void *temp;
if (ref->used < 1)
size = 1; /* Minimum size is 1 */
else
size = (ref->used | 7) + 9; /* Next multiple of 8 */
temp = realloc(ref->array, size * sizeof ref->array[0]);
if (!temp) {
fprintf(stderr, "table_push_array(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ref->array = temp;
for (i = ref->size; i < size; i++)
array_init(ref->array + i); /* array_init(&(ref->array[i])); */
ref->size = size;
}
ref->array[ref->used] = *one; /* "shallow copy" */
ref->used++;
}
The corresponding pop operation should be pretty obvious by now:
array *table_pop_array(table *ref)
{
array retval = ARRAY_INIT;
if (!ref) {
fprintf(stderr, "table_pop_array(): NULL table!\n");
exit(EXIT_FAILURE);
}
if (ref->used < 1) {
fprintf(stderr, "table_pop_array(): Table is empty, no arrays to pop!\n");
exit(EXIT_FAILURE);
}
/* Decrement the used count, so it refers to the last array in the table. */
ref->used--;
/* Shallow copy the array. */
retval = ref->array[ref->used];
/* Init, but do not free, the array in the table. */
array_init(ref->array + ref->used); /* array_init(&(ref->array[ref->used)); */
/* Return the array. */
return retval;
}
There is a "trick" in table_pop_array() above, that you should understand. While we cannot return pointers to local variables, we can return structures. In the above case, the structure describes the array, and the pointer in it does not refer to a local variable, but to a dynamically allocated array of items. Structure types can be assigned just as normal scalar types (like int or double); it is basically the same as if you assigned each member separately.
Overall, you should notice I have not used a single malloc() call. This is because realloc(NULL, size) is equivalent to malloc(size), and simply initializing unused pointers to NULL makes everything simpler.
When a table is grown (reallocated), we do need to initialize all the new arrays, because of the above realloc() use pattern.
The above approach does not preclude direct access to specific arrays in the table, or specific items in an array. If you intend to implement such functions, two helper functions similar to
void table_need_arrays(table *ref, const size_t size);
void array_need_items(array *ref, const size_t size);
that ensure that the table has room for at least size arrays, and an array has room for at least size items. They are also useful when pushing multiple items or arrays consecutively, as then one can do e.g. table_need_arrays(&mytable, mytable.used + 10); to ensure there is room for additional 10 arrays in the table.
All throughout the functions above, you can see notation name_of_array + index, and a comment with corresponding &(name_of_array[index]). This is because the two notations are equivalent: pointer to the index'th element in name_of_array.
I didn't bother to compile-check the above code, so there might be typos hidden in there. (This too is intentional, because I want you to understand the code, and not just copy it and use it as your own without understanding any of the details.) However, the logic is sound. So, if you find a typo or issue, let me know in a comment, and I shall fix.
I am using realloc to adjust the size of an array of structs containing 3 points x, y and z. This struct is encapsulated inside another struct that contains the array, the length of the array and a "reserved" value that is used for a pre-allocation strategy for even faster performance when it is evident that more structs of points will be appended to the struct array.
I am compiling with a Makefile that looks like this:
CFLAGS = -g -Wall
LIBS = -lm
default: echo "You must specify a target, e.g. file1, file2"
file2:
gcc $(CFLAGS) -o $# test.c file2.c $(LIBS)
I have a function to initialize an empty array structure, one to reset the array to be empty and free any dynamically allocated memory, one to append a point to the end of the array and one to remove a point designated by the index value.
I am getting two errors that I cannot find the cause of. One is that my code returns a non-zero status code of 1 and the other is the length seems to be off by one when I append a few thousand points.
I am letting the append function do all the work but if I should be allocating dynamic memory in initialization, please tell me so. I am pretty sure that my reset and remove functions are working as they are supposed to. Please take a look at append as well.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <assert.h>
typedef struct point
{
int x, y, z;
} point_t;
typedef struct
{
// number of points in the array
size_t len;
// pointer to an array of point_t structs
point_t* points;
size_t reserved;
} point_array_t;
void point_array_initial( point_array_t* pa )
{
assert(pa);
pa->len = 0;
pa->reserved = 0;
pa->points=NULL;
}
void point_array_reset( point_array_t* pa )
{//just free the array and set pa to NULL
assert(pa);
pa->points = memset(pa->points, 0, sizeof(point_t)*(pa->len));
pa->len = 0;
pa->reserved=0;
free(pa->points);
pa->points=NULL;
}
int point_array_append( point_array_t* pa, point_t* p )
{
assert(pa);
assert(p);
if(pa == NULL)//something wrong with intialization or reset
{
return 1;
}
if(p == NULL)//nothing to append
{
return 1;
}
//append the first point
if(pa->len == 0)
{
pa->len=1;
pa->reserved=pa->len*2;
pa->points = malloc(sizeof(point_t)* (pa->reserved));
if(pa->points == NULL)//malloc failed
{
return 1;
}
pa->points[pa->len-1].x = p->x;
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
if (pa->reserved > pa->len )
{
pa->len+=1;
pa->points[pa->len-1].x = p->x;//insert at index 0
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
//when we run out of space in reserved (len has caught up)
else if(pa->reserved == pa->len)
{
pa->len+=1;
pa->reserved=pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->reserved));//doubling size of array
pa->points[pa->len-1].x = p->x;//TODO: change formula to find insertion point
pa->points[pa->len-1].y = p->y;
pa->points[pa->len-1].z = p->z;
}
return 0;
}
int point_array_remove( point_array_t* pa, unsigned int i )
{
assert(pa);
if (i >= pa->len)//out of bounds
{
return 1;
}
if(pa->len==0)//0 elements trying to remove from empty array
{
//pa->len=0;
//free(pa->points);
//pa->points=NULL;
return 1;
}
else if(pa->len ==1)//remove only element
{
pa->len-=1;//no copying required, just shorten
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
//free(pa->points);
//pa->points=NULL;
}
else//array size is longer than 1 or 0
{
pa->points[i].x = pa->points[pa->len-1].x;
pa->points[i].y = pa->points[pa->len-1].y;
pa->points[i].z = pa->points[pa->len-1].z;
pa->len-= 1;//shorten array size
pa->reserved = pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
}
return 0;
}
an else is missing after the if(pa->len == 0) body in the append function: the first point is appended twice.
Note that you have too many special cases in this function. It can be simplified into just a one test: if the array is too small, reallocate it, and append the point.
Other simplifications are possible:
the test if (pa->len == 0)//0 elements trying to remove from empty array is redundant with the previous one.
take advantage of the fact that realloc(NULL, size) is equivalent to malloc(size) and realloc(p, 0) to free(p), and free(NULL) is OK.
beware that realloc() may fail, even when shrinking the block.
you should only shrink the array when it becomes too sparse, not for every call to point_array_remove.
Here is a simpler version:
#include <assert.h>
#include <stdlib.h>
typedef struct point {
int x, y, z;
} point_t;
typedef struct {
size_t len; // number of valid points in the array
size_t reserved; // allocated number of points in the array
point_t *points; // pointer to an array of point_t structs
} point_array_t;
void point_array_initial(point_array_t *pa) {
assert(pa);
pa->len = 0;
pa->reserved = 0;
pa->points = NULL;
}
void point_array_reset(point_array_t *pa) {
assert(pa);
free(pa->points);
pa->len = 0;
pa->reserved = 0;
pa->points = NULL;
}
int point_array_append(point_array_t *pa, const point_t *p) {
point_t *points;
assert(pa);
assert(p);
// no need to test pa nor p, asserts would already abort
points = pa->points;
if (pa->len >= pa->reserved || points == NULL) {
// reallocate of points array is too small
size_t newsize = pa->reserved;
if (newsize < pa->len)
newsize = pa->len;
if (newsize < 1)
newsize = 1;
newsize += newsize;
points = realloc(points, newsize * sizeof(*points);
if (points == NULL)
return 1;
pa->points = points;
pa->reserved = newsize;
}
// append point structure
points[pa->len++] = *p;
return 0;
}
int point_array_remove(point_array_t *pa, unsigned int i) {
point_t *points;
assert(pa);
if (i >= pa->len || pa->points == NULL) { //out of bounds or invalid array
return 1;
}
if (pa->len - i > 1) {
memmove(&pa->points + i, &pa->points + i + 1,
sizeof(*pa->points) * (pa->len - i - 1));
}
pa->len--;
if (pa->reserved >= pa->len * 3) {
size_t newsize = pa->len * 2;
// shorten the array with care.
// note that the array will be freed when it becomes empty
// no special case needed.
points = realloc(pa->points, sizeof(*points) * newsize);
if (points != NULL) {
pa->points = points;
pa->reserved = newsize;
}
}
return 0;
}
In addition to the error pointed out by chqrlie, here are a few additional thoughts on your code.
A better choice of CFLAGS for non-debug builds would be
-Wall -Wextra -O3
add -pedantic for a few additional warnings and you can use -Ofast with gcc >= 4.6.
Never realloc the pointer itself, If realloc fails, NULL is returned and you have lost the reference to your original memory block -- and created a memory leak because you no longer have the beginning address of the block to free. Don't increment len or reserved until you validate realloc succeeded. Instead, always use a temporary pointer and increment values only on success, e.g.
else if(pa->reserved == pa->len)
{
void *tmp = realloc(pa->points, sizeof(point_t)*(pa->len + 1) * 2);
if (!tmp) {
/* handle error - exit or return */
}
pa->points = tmp;
pa->len+=1;
pa->reserved=pa->len*2;
}
The following looks like a problem if you are simply wanting to shorten the array by one:
else if(pa->len ==1)//remove only element
{
pa->len-=1;//no copying required, just shorten
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
//free(pa->points);
//pa->points=NULL;
}
else//array size is longer than 1 or 0
{
pa->points[i].x = pa->points[pa->len-1].x;
pa->points[i].y = pa->points[pa->len-1].y;
pa->points[i].z = pa->points[pa->len-1].z;
pa->len-= 1;//shorten array size
pa->reserved = pa->len*2;
pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
}
In the else above you are assigning the previous point to the last, then chopping off the last -- either I don't understand what you are trying to accomplish, or it's not doing what you think it is. In either case, unless you have some compelling reason for wanting to realloc to shorten the array by one (I'd wait until all add/remove operations are done and then call a final realloc on len element to exactly size your memory use). Instead, I would replace the entirety of the above with:
else
pa->len -= 1;
No need to mess with anything else. You effectively ignore the data in the last row -- which isn't hurting anything, until your next add overwrites the values.