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.
Related
I'm trying to implement an integer vector in C. The vector pointer created in vector_copy is aroung 0x3 or 0x2, which causes segmentation fault. Why is that happenning?
This really baffles me since I have a similar implementation for vector_new.
typedef struct vector {
int* items;
size_t size;
size_t max;
} vector;
vector* vector_copy(vector* v) {
vector* v2;
memcpy(v2->items,v->items,v->max * sizeof(int*));
if (v2->items == NULL) {
return NULL;
}
v2->size = v->size;
v2->max = v->max;
return v2;
}
vector* vector_new(size_t initial_capacity) {
vector* newv;
newv->items = malloc(initial_capacity * sizeof(int*));
if (newv->items == NULL) {
return NULL;
}
newv->size = 0;
newv->max = initial_capacity;
return newv;
}
int main() {
vector* v;
vector* v2;
v = vector_new(4);
//new vector with capacity 4 and size 0
vector_push(v, 1);
vector_push(v, 2);
//1 and 2 are pushed back
v2=vector_copy(v);
//vector is supposed to be copied
vector_free(v);
vector_free(v2);
return 0;
}
vector* v2;
memcpy(v2->items,v->items,v->max * sizeof(int*));
This is not a good idea. You have basically created a pointer v2 that points to some arbitrary location (because you haven't initialised it), with the v2 pointer value probably being whatever was left over on the stack from some previous operation(a). Then, by copying bytes to that arbitrary location, all sorts of weirdness may ensue.
You need to actually allocate some memory for this new vector, with something like:
vector *v2 = vector_new(v->max);
memcpy(v2->items, v->items, v->max * sizeof(int));
You'll (hopefully) also notice I've changed the sizeof to use int rather than int*. I suspect that items points to an array of the former in which case that's the correct value to use.
So the following function is a better starting point:
vector *vector_copy(vector* v) {
// Use vector_new to get empty one with exact same properties.
vector *v2 = vector_new(v->max);
if (v2 == NULL) return NULL;
// Copy data in to it and return.
memcpy(v2->items, v->items, v->max * sizeof(int));
v2->size = v->size;
return v2;
}
You also have the exact same issues (no structure allocation and wrong sizeof) in your new function, which can be fixed with:
vector *vector_new(size_t initial_capacity) {
// Allocate a vector structure, fail if no go.
vector *newv = malloc(sizeof(vector));
if (newv == NULL) return NULL;
// Allocate the data area, free structure and fail if no go.
newv->items = malloc(initial_capacity * sizeof(int));
if (newv->items == NULL) {
free(newv);
return NULL;
}
// Set up everything needed and return it.
newv->size = 0;
newv->max = initial_capacity;
return newv;
}
The fact that this function sets the maximum based on initial_capacity relieves you of the necessity of setting max in vector_copy().
(a) I state "probably" because of the way many implementations work in terms of reusing stack frame memory regions. However, it's by no means guaranteed, just one possibility. You should just assume that it will have some random value and will therefore behave appropriately (or inappropriately, depending on your viewpoint).
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
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.
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;
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.