Weird issue when reallocating memory in C - c

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

Related

Realloc does not work (following the instruction in cs50 lecture 5)

I'm going to add a new value 4 to the list array.
The original values in list array are 1,2,3.
But when i run the following code, i didn't get 1,2,3,4 but several random numbers.
Each time I run I get different output.
Can someone help me figure out what's going wrong here?
Thanks a lot.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3 * sizeof(int));
//如果直接写int list[3] 就没有办法修改大小了
if (list == NULL)
{
free(list);
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
//resize the old array to be of size 4
//用realloc指定下old array,无需再做copy的工作
int *tmp = realloc(list, 4 * sizeof(int));
if (list == NULL)
{
free(list);
//a safety check, free the original list
return 1;
}
tmp[3] = 4;
//free old array
free(list); //这里就可以free之前的list了
//remember new array
list = tmp;
//所以不需要在free(tmp), free(list)相当于free(tmp)
//print new array
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//free new array
free(list);
return 0; //最后记得加上这个
}
the output is like this:
1609039888
25764
2043
4
try this code
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3 * sizeof(int));
//如果直接写int list[3] 就没有办法修改大小了
if (list == NULL)
{
free(list);
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
//resize the old array to be of size 4
//用realloc指定下old array,无需再做copy的工作
int *tmp = realloc(list, 4 * sizeof(int));
if (list == NULL)
{
free(list);
//a safety check, free the original list
return 1;
}
tmp[3] = 4;
//free old array
//free(list); //这里就可以free之前的list了 // this free the last memory location which empty the array.
//remember new array
list = tmp;
//所以不需要在free(tmp), free(list)相当于free(tmp)
//print new array
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//free new array
free(list);
return 0; //最后记得加上这个
}
There's a fundamental misunderstanding on how dynamic memory management actually works...
At first, if an allocation fails (malloc returning a null pointer), then there's nothing to free anyway, so you simply don't need to (even though it's legal – then effectively a no-op...).
int* list = malloc(...);
if(!list) // shorter for list == NULL
{
return -1;
}
Then realloc replaces the old array for you already! You can imagine it to work like this:
void* realloc(void* oldData, size_t desired)
{
// retain currently allocated memory size from pointer
// that's OS/compiler specific knowledge, usually stored somewhere
// in front of the memory the pointer points to, but not (legally)
// accessible by you...
size_t old = ...;
if(desired <= old)
{
return oldData;
}
void* newData = malloc(desired);
if(newData)
{
memcpy(newData, oldData, oldSize);
free(oldData); // !!!
}
return newData;
}
Note how the old data remains intact if re-allocation fails, but gets deleted on success!
Correct usage of realloc thus looks as follows:
int* tmp = realloc(list, desiredSize);
if(!tmp)
{
// appropriate error handling
// usually you cannot meaningfully go on anyway, so let's just exit
// but HERE list still points to valid memory, so clean up first:
free(list);
return -1;
}
// we can safely use the temporary as the list now; note that the old
// memory already HAS been deleted!
list = tmp;
// and now we simply use it:
list[3] = 4;
free(list); // when done

Problems with realloc, makes program crash

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

C: Stack element overwritten by a function call

I'm doing a school assignment, I've I've run into 2 problems. I have to simulate stacks, with arrays.
My current code is as follows:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int capacity;
int * array;
int size;
} stack_tt;
int pop(stack_tt * stack_p);
void push(stack_tt * stack_p, int value);
int top(stack_tt * stack_p);
stack_tt * newStack(void);
int empty(stack_tt * stack_p);
int main() {
stack_tt * myStack = newStack();
push(myStack, 123);
push(myStack, 99);
push(myStack, 4444);
while (!empty(myStack)) {
int value;
value = pop(myStack);
printf("popped: %d\n", value);
}
return 0; }
stack_tt * newStack(){
stack_tt * newS = malloc(sizeof(stack_tt) * 20);
(*newS).capacity = 1;
(*newS).size = 0;
return newS;
}
void push(stack_tt * stack_p, int value){
if ((*stack_p).size >= (*stack_p).capacity) {
(*stack_p).capacity*=2;
//realloc(stack_p, stack_p->capacity * sizeof(stack_tt));
}
(*stack_p).array = &value;
(*stack_p).size++;
}
int pop(stack_tt * stack_p){
(*stack_p).size--;
int fap = *(*stack_p).array;
return fap;
}
int empty(stack_tt * stack_p){
if ((*stack_p).size >= 1)
return 0;
return 1;
}
Fist of, when I call the line
while(!empty(myStack))
It changes the value in my array to 1.
secondly I'm not able to change individual values in my array, whenever I try things like:
(*stack_p).array[0] = value;
It doesn't know where in the memory to look.
I hope someone is able to help me out :)
There are a couple of problems with the code as I see it.
Lets take the push function where you do
(*stack_p).array = &value;
That will make the array structure member point to the local variable value, and once the function returns the variable cease to exist leaving you with a stray pointer and using that pointer will lead to undefined behavior.
The second problem with that code is that your stack will only be pointing (illegally) to the last element added.
You must allocate memory explicitly for array and use capacity to keep track of how much memory is allocated. The use size as an index into the allocated array for the pushing and popping. Something like
stack_tt * newStack(){
stack_tt * newS = malloc(sizeof(stack_tt)); // Only allocate *one* structure
newS->capacity = 0; // Start with zero capacity
newS->size = 0;
newS->array = NULL;
return newS;
}
void push(stack_tt * stack_p, int value){
if (stack_p->size + 1 > stack_p->capacity){
// Increase capacity by ten elements
int new_capacity = stack_p->capacity + 10;
int * temp_array = realloc(stack_p->array, new_capacity * sizeof(int));
if (temp_srray == NULL)
return;
stack_p->capacity = new_capacity;
stack_p->array = temp_array;
}
stack_p->array[stack_p->size++] = value;
}
int pop(stack_tt * stack_p){
if (stack_p->size > 0)
return stack_p->array[--stack_p->size];
return 0;
}
int empty(stack_tt * stack_p){
return stack_p->size == 0;
}
There is no need to allocate space for 20 structs of type stack_tt, you only need to allocate space for one:
stack_tt * newS = malloc(sizeof(stack_tt));
however you need to allocate space for elements of the struct member array:
newS->array = malloc( sizeof(int)*20);
newS->size = 0;
newS->capacity = 20;
now you can use the array member.
When you push a value to the 'stack', you shouldn't overwrite the array member with the address of the local variable, that doesn't make sense and will cause undefined behavior in addition of loosing the previously allocated memory. Instead simply assign the value to the member array, in the function push:
stack_p->array[stack_p->size] = value;
stack_p->size++;
Similarly when you pop an element, take the current element from the member array:
stack_p->size--;
int fap = stack_p->array[stack_p->size];
The rest of the functions and code should be fixed in the same manner.
You're code is good, but probably you didn't understand the usage of realloc:
//realloc(stack_p, stack_p->capacity * sizeof(stack_tt));
This function returns a pointer to the newly allocated memory, or NULL if the request fails.
The realloc (as the function suggests) takes the memory pointed by the pointer you pass, and copies that memory block in a new and resized block. So the right code should be.
stack_p->array = realloc(stack_p->array, stack_p->capacity * sizeof(stack_tt));
This other line is wrong:
(*stack_p).array = &value;
Change it with:
stack_p->array[stack_p->size] = value;
Another little suggestion, every (*stack_p). can be replaced by stack_p->, which is more elegant.
In the newStack() you're mallocing 20 structs which is kinda useless. You just need one.
Then you should malloc the array for the first time:
newS->array = malloc(sizeof(int));
newS->capacity = 1;

Strange behaviour on Realloc: invalid next size [duplicate]

This question already has an answer here:
free char*: invalid next size (fast) [duplicate]
(1 answer)
Closed 8 years ago.
I know there are tons of other realloc questions and answers and I have read almost all of them, but I still couldn't manage to fix my problem.
I decided to stop trying when I accidentaly discovered a very strange behaviour of my code.
I introduced a line to try something, but although I don't use the value of newElems in main, the line changes the behaviour.
When the line is commented, the code fails at first realloc. Including the line, the first realloc works. (it still crashes on the second one).
Any ideas on what might be happening?
int main(int argc, char** argv) {
Pqueue q = pqueue_new(3);
Node a = {.name = "a"}, b = {.name = "b"},
c = {.name = "c"}, d = {.name = "d"};
push(& q, & a, 3);
// the next one is the strange line: as you can see, it doesn't modify q
// but commenting it out produces different behaviour
Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
push(& q, & b, 5);
push(& q, & c, 4);
char s[5];
Node* n;
for (int i = 1; i <= 65; ++i) {
sprintf(s, "%d", i);
n = malloc(sizeof *n);
n->name = strdup(s);
push(& q, n, i);
}
Node* current = NULL;
while ((current = pop(& q))) {
printf("%s ", current->name);
}
return 0;
}
and the push function:
void push(Pqueue* q, Node* item, int priority) {
if (q->size >= q->capacity) {
if (DEBUG)
fprintf(stderr, "Reallocating bigger queue from capacity %d\n",
q->capacity);
q->capacity *= 2;
Pqueue_elem* newElems = realloc(q->elems,
q->capacity * sizeof *newElems);
check(newElems, "a bigger elems array");
q->elems = newElems;
}
// append at the end, then find its correct place and move it there
int idx = ++q->size, p;
while ((p = PARENT(idx)) && priority > q->elems[p].priority) {
q->elems[idx] = q->elems[p];
idx = p;
}
// after exiting the while, idx is at the right place for the element
q->elems[idx].data = item;
q->elems[idx].priority = priority;
}
The pqueue_new function:
Pqueue pqueue_new(unsigned int size) {
if (size < 4)
size = 4;
Pqueue* q = malloc(sizeof *q);
check(q, "a new queue.");
q->capacity = size;
q->elems = malloc(q->capacity * sizeof *(q->elems));
check(q->elems, "queue's elements");
return *q;
}
realloc will change the amount of memory that is allocated, if needed. It is also free to move the data to another place in memory if that's more efficient (avoiding memory fragmentation).
The function, then, returns a new pointer to the new location in memory where your data is hiding. You're calling realloc, and allocating (probably) four times as much memory as before, so it's very likely that that allocated memory is situated elsewhere in memory.
In your comment, you said realloc works like free + malloc. Well, in some cases it can behave similarly, however: realloc and free are different functions, that do different tasks. Both are functions that manage the dynamic memory, so yes, obviously there are similarities, and in the case of realloc, sometimes they can seem to be doing the same thing, however: As I explained here, realloc and free are fundamentally different functions
However, by not assigning the return value of realloc to q.elems, you're left with a pointer to a memory address that is no longer valid. The rest of your program can, and probably does, exhibit signs of undefined behaviour, then.
Unless you show some more code, I suspect this will take care of the problem:
//change:
Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
//to
q.elems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
Or better yet, check for NULL pointers:
Pqueue_elem* newElems = realloc(q.elems, 4 * q.capacity * sizeof *newElems);
if (newElems == NULL)
exit( EXIT_FAILURE );// + fprintf(stderr, "Fatal error...");
q.elems = newElems;//<-- assign new pointer!
Looking at your pqueue_new function, I would suggest a different approach. Have it return the pointer to Pqueue. You're working with a piece of dynamic memory, treat it accordingly, and have your code reflect that all the way through:
Pqueue * pqueue_new(size_t size)
{//size_t makes more sense
if (size < 4)
size = 4;
Pqueue* q = malloc(sizeof *q);
check(q, "a new queue.");
q->capacity = size;
q->elems = malloc(q->capacity * sizeof *(q->elems));
check(q->elems, "queue's elements");
return q;
}
Alternatively, pass the function a pointer to a stack variable:
void pqueue_new(Pqueue *q, size_t size)
{
if (q == NULL)
{
fprintf(stderr, "pqueue_new does not do NULL pointers, I'm not Chuck Norris");
return;//or exit
}
if (size < 4)
size = 4;
check(q, "a new queue.");
q->capacity = size;
q->elems = malloc(q->capacity * sizeof *(q->elems));
check(q->elems, "queue's elements");
}
//call like so:
int main ( void )
{
Pqueue q;
pqueue_new(&q, 3);
}
Those would be the more common approaches.
Thank you all for the suggestions! I wouldn't have solved it without them,
The strange behaviour was caused by an off by one error. I was reallocating the queue only when q->size >= q->capacity, but since q was indexed from 0, it meant that before realloc I was writing in a forbidden location (q->elems[q->size]), which messed everything up.

Dynamic Array in C - realloc

I know how to build Dynamically allocated arrays, but not how to grow them.
for example I have the following interface..
void insertVertex( vertex p1, vertex out[], int *size);
This method takes a vertex and stores it into the out array. After storing the vertex I increase the count of length for future calls.
p1 - is the vertex I'm going to add.
out[] - is the array I need to store it in (which is always full)
length - the current length
Vertex is defined as..
typedef struct Vertex{
int x;
int y;
} Vertex;
This is what I'm using in Java..
Vertex tempOut = new Vertex[size +1];
//Code to deep copy each object over
tempOut[size] = p1;
out = tempOut;
This is what I believed I could use in c..
out = realloc(out, (*size + 1) * sizeof(Vertex));
out[(*size)] = p1;
However, I keep on receiving an error message that the object was not allocated dynamically.
I found a solution that will resolve this.. Instead of using Vertex* I was going to switch to Vertex** and store pointers vs. vertex. However, after switching everything over I found out that I over looked the fact that the unit test will be providing me a Vertex out[] that everything has to be stored in.
I have also tried the following with no luck.
Vertex* temp = (Vertex *)malloc((*size + 1) * sizeof(Vertex));
for(int i = 0; i < (*size); i++)
{
temp[i] = out[i];
}
out = temp;
However, no matter what I do when I test after both of these the array returned has not changed.
Update - Requested information
out - is defined as an array of Vertex (Vertex out[])
It is originally built with the number of vertex in my polygon. For example.
out = (Vertex *)malloc(vertexInPolygon * sizeof(Vertex))
Where vertexInPolygon is an integer of the number of vertex in the polygon.
length was a typo that should have been size.
Size is an integer pointer
int *size = 0;
Each time a vertex is in the clipping plane we add it to the array of vertex and increase the size by one.
Update
To better explain myself I came up with a short program to show what I'm trying to do.
#include <stdio.h>
#include <stdlib.h>
typedef struct Vertex {
int x, y;
} Vertex;
void addPointerToArray(Vertex v1, Vertex out[], int *size);
void addPointerToArray(Vertex v1, Vertex out[], int *size)
{
int newSize = *size;
newSize++;
out = realloc(out, newSize * sizeof(Vertex));
out[(*size)] = v1;
// Update Size
*size = newSize;
}
int main (int argc, const char * argv[])
{
// This would normally be provided by the polygon
int *size = malloc(sizeof(int)); *size = 3;
// Build and add initial vertex
Vertex *out = (Vertex *)malloc((*size) * sizeof(Vertex));
Vertex v1; v1.x = 1; v1.y =1;
Vertex v2; v2.x = 2; v2.y =2;
Vertex v3; v3.x = 3; v3.y =3;
out[0] = v1;
out[1] = v2;
out[2] = v3;
// Add vertex
// This should add the vertex to the last position of out
// Should also increase the size by 1;
Vertex vertexToAdd; vertexToAdd.x = 9; vertexToAdd.y = 9;
addPointerToArray(vertexToAdd, out, size);
for(int i =0; i < (*size); i++)
{
printf("Vertx: (%i, %i) Location: %i\n", out[i].x, out[i].y, i);
}
}
One long-term problem is that you are not returning the updated array pointer from the addPointerToArray() function:
void addPointerToArray(Vertex v1, Vertex out[], int *size)
{
int newSize = *size;
newSize++;
out = realloc(out, newSize * sizeof(Vertex));
out[(*size)] = v1;
// Update Size
*size = newSize;
}
When you reallocate space, it can move to a new location, so the return value from realloc() need not be the same as the input pointer. This might work while there is no other memory allocation going on while you add to the array because realloc() will extend an existing allocation while there is room to do so, but it will fail horribly once you start allocating other data while reading the vertices. There are a couple of ways to fix this:
Vertex *addPointerToArray(Vertex v1, Vertex out[], int *size)
{
int newSize = *size;
newSize++;
out = realloc(out, newSize * sizeof(Vertex));
out[(*size)] = v1;
// Update Size
*size = newSize;
return out;
}
and invocation:
out = addPointerToArray(vertexToAdd, out, size);
Alternatively, you can pass in a pointer to the array:
void addPointerToArray(Vertex v1, Vertex **out, int *size)
{
int newSize = *size;
newSize++;
*out = realloc(*out, newSize * sizeof(Vertex));
(*out)[(*size)] = v1;
// Update Size
*size = newSize;
}
and invocation:
out = addPointerToArray(vertexToAdd, &out, size);
Neither of these rewrites addresses the subtle memory leak. The trouble is, if you overwrite the value you pass into realloc() with the return value but realloc() fails, you lose the pointer to the (still) allocated array - leaking memory. When you use realloc(), use an idiom like:
Vertex *new_space = realloc(out, newSize * sizeof(Vertex));
if (new_space != 0)
out = new_space;
else
...deal with error...but out has not been destroyed!...
Note that using realloc() to add one new item at a time leads to (can lead to) quadratic behaviour. You would be better off allocating a big chunk of memory - for example, doubling the space allocated:
int newSize = *size * 2;
If you are worried about over-allocation, at the end of the reading loop, you can use realloc() to shrink the allocated space to the exact size of the array. However, there is then a bit more book-keeping to do; you need to values: the number of vertices allocated to the array, and the number of vertices actually in use.
Finally, for now at least, note that you should really be ruthlessly consistent and use addPointerToArray() to add the first three entries to the array. I'd probably use something similar to this (untested) code:
struct VertexList
{
size_t num_alloc;
size_t num_inuse;
Vertex *list;
};
void initVertexList(VertexList *array)
{
// C99: *array = (VertexList){ 0, 0, 0 };
// Verbose C99: *array = (VertexList){ .num_inuse = 0, .num_alloc = 0, .list = 0 };
array->num_inuse = 0;
array->num_alloc = 0;
array->list = 0;
}
void addPointerToArray(Vertex v1, VertexList *array)
{
if (array->num_inuse >= array->num_alloc)
{
assert(array->num_inuse == array->num_alloc);
size_t new_size = (array->num_alloc + 2) * 2;
Vertex *new_list = realloc(array->list, new_size * sizeof(Vertex));
if (new_list == 0)
...deal with out of memory condition...
array->num_alloc = new_size;
array->list = new_list;
}
array->list[array->num_inuse++] = v1;
}
This uses the counter-intuitive property of realloc() that it will do a malloc() if the pointer passed in is null. You can instead do a check on array->list == 0 and use malloc() then and realloc() otherwise.
You might notice that this structure simplifies the calling code too; you no longer have to deal with the separate int *size; in the main program (and its memory allocation); the size is effectively bundled into the VertexList structure as num_inuse. The main program might now start:
int main(void)
{
VertexList array;
initVertexList(&array);
addPointerToArray((Vertex){ 1, 1 }, &array); // C99 compound literal
addPointerToArray((Vertex){ 2, 2 }, &array);
addPointerToArray((Vertex){ 3, 3 }, &array);
addPointerToArray((Vertex){ 9, 9 }, &array);
for (int i = 0; i < array->num_inuse; i++)
printf("Vertex %d: (%d, %d)\n", i, array->list[i].x, array->list[i].y, i);
return 0;
}
(It is coincidental that this sequence will only invoke the memory allocation once because the new size (old_size + 2) * 2 allocates 4 elements to the array the first time. It is easy to exercise the reallocation by adding a new point, or by refining the formula to (old_size + 1) * 2, or ...
If you plan to recover from memory allocation failure (rather than just exiting if it happens), then you should modify addPointerToArray() to return a status (successful, not successful).
Also, the function name should probably be addPointToArray() or addVertexToArray() or even addVertexToList().
I have a few suggestions for your consideration:
1. Don't use the same input & output parameter while using realloc as it can return NULL in case memory allocation fails & the memory pointed previously is leaked. realloc may return new block of memory (Thanks to #Jonathan Leffler for pointing out, I had missed this out). You could change your code to something on these lines:
Vertex * new_out = realloc(out, newSize * sizeof(Vertex));
if( NULL != new_out )
{
out = new_out;
out[(*size)] = v1;
}
else
{
//Error handling & freeing memory
}
2. Add NULL checks for malloc calls & handle errors when memory fails.
3. Calls to free are missing.
4. Change the return type of addPointerToArray() from void to bool to indicate if the addition is successful. In case of realloc failure you can return failure say, false else you can return success say, true.
Other observations related to excessive copies etc, are already pointed out by #MatthewD.
And few good observations by #Jonathan Leffler (:
Hope this helps!
Your sample program works fine for me. I'm using gcc 4.1.1 on Linux.
However, if your actual program is anything like your sample program, it is rather inefficient!
For example, your program copies memory a lot: structure copies - initialising out, passing vertices to addPointerToArray(), memory copies via realloc().
Pass structures via a pointer rather than by copy.
If you need to increase the size of your list type a lot, you might be better off using a linked list, a tree, or some other structure (depending on what sort of access you require later).
If you simply have to have a vector type, a standard method of implementing dynamically-sized vectors is to allocate a block of memory (say, room for 16 vertices) and double its size everytime you run out of space. This will limit the number of required reallocs.
Try these changes , it should work.
void addPointerToArray(Vertex v1, Vertex (*out)[], int *size)
{
int newSize = *size;
newSize++;
*out = realloc(out, newSize * sizeof(Vertex));
*out[(*size)] = v1;
// Update Size
*size = newSize;
}
and call the function like
addPointerToArray(vertexToAdd, &out, size);
There is a simple way to fix these type of issue (you might already know this). When you pass a argument to a function, think what exactly goes on to the stack and then combine the fact that what ever changes you make to variables present on stack would vanish when come out the function. This thinking should solve most of the issues related to passing arguments.
Coming to the optimization part, picking the right data structure is critical to the success of any project. Like somebody pointed out above, link list is a better data structure for you than the array.

Resources