realloc(): invalid next size and double free - c

As a homework, I'm supposed to create 2 functions that enable you to push and pop elements to an array that acts as a queue. We're supposed to do this dynamically allocating memory. My program is almost working, but sometimes when adding and removing too many elements, I get errors like "realloc(): invalid next size", double free (when I've only called the free function once) and some of the elements in the beginning of the queue are set to 0. For instance, if I first add 100 elements, then remove 90 and try to add another 20, I get "free(): invalid next size (fast): 0x0000000001ea6010".
What am I doing wrong here?
According to suggestions below I changed my functions to take a double pointer as an input for the array. That, however, now gives me a Segmentation fault - which means now I don't know what to look for at all...
#include <stdio.h>
#include <stdlib.h>
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
if (*lastElementIdx >= *totalElements) { // check if memorry is sufficient, otherwise double
*totalElements *= 2;
int* temp = realloc(arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // just in case realloc fails
printf("Allocation error\n");
} else {
*arr = temp;
}
}
if (*lastElementIdx <= *totalElements) {
*lastElementIdx += 1; // once everything is done: add element
*arr[*lastElementIdx] = element;
}
}
int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
if (*lastElementIdx > -1) { // if queue is not empty...
int deleted = *arr[0]; // save deleted value first (in case it's still needed)
for (int i = 0; i <= *lastElementIdx; i++) { // shift all elements
*arr[i] = *arr[i + 1];
}
*lastElementIdx -= 1; // index is now decreased by 1
if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) { // cut memory in half if not needed
*totalElements /= 2;
*arr = realloc(arr, (*totalElements * sizeof(int)));
int* temp = realloc(arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // in case realloc fails
printf("Allocation error\n");
return 0;
} else {
*arr = temp;
}
}
return deleted;
} else { // if queue is empty, print that there's nothing to dequeue
printf("There are no elements inside the queue\n");
return 0;
}
}
void printQueue(int arr[], int lastElementIdx) {
for (int i = 0; i <= lastElementIdx; i++) { // print entire queue
printf("[%d] = %d\n", i, arr[i]);
}
printf("\n");
}
int main (void) {
size_t totalElements = 10; // number of needed elements at the time
int lastElementIdx = -1; // index of last element in queue at the time
int *arr = calloc(totalElements, sizeof(int));
int **arrpointer = &arr;
for (int i = 1; i < 101; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
for (int i = 0; i < 90; i++) {
dequeue(arrpointer, &lastElementIdx, &totalElements);
}
printQueue(arr, lastElementIdx);
for (int i = 1; i < 21; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
free(arr);
return EXIT_SUCCESS;
}

When you expand or contract the storage for your queue, you need to provide a pointer to the storage back to the caller. This is because realloc() does not necessarily resize a memory block in-place -- it may create a new, differently sized block elsewhere. It is permitted to do so even when it resizes to a smaller block, not only when it resizes to a larger one.
Your usage of variable temp gives the appearance that you are aware of this issue, but as #DerkHermann first observed, you mishandle the resulting pointer. Perhaps you meant to write something along the lines of
arr = temp;
instead. Even that is not sufficient, however. C has only pass-by-value, so if you modify the value of function parameter arr, that modification is visible only in the function (which receives in arr a copy of the value the caller passes). In the event that realloc() allocates a new block, that leaves the caller with an invalid pointer.
If you want your enqueue() and dequeue() functions to be able to resize the storage for the queue, then you must pass the pointer to that storage indirectly. The most straightforward way of doing that, given where you are now, would be to pass a double pointer, so that you can modify its referrent:
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
/* ... */
*arr = temp;
/* ... */
}
Observe, however, that you are passing three separate pointers that among them represent the state of the queue. It would be cleaner to create a struct type that combines those details in one package, and to pass a pointer to an object of that type:
struct queue {
int *arr;
size_t capacity;
size_t last_element_index;
};
void enqueue(struct queue *queue, int element) {
/* ... */
queue->arr = temp;
/* ... */
}

Maybe it's not the only problem, but at least the following line does not what you seem to expect:
*temp = *arr;
It looks as if you want to replace arr with the result of the realloc, delivering it back to the calling function (as with your other inout arguments). But, arr is not an inout argument: It is an array of integers, not a pointer to an array of integers. What you are actually doing with your above line of code is to copy the first element of arr to the newly allocated memory range. That newly allocated memory range temp, then, is nevertheless forgotten, creating a memory leak.

Adding a double pointer to reallocate the space in the right places, changing the comparing function with size_t totalElements and fixing a few additional mistakes finally did the trick.
#include <stdio.h>
#include <stdlib.h>
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
if (*lastElementIdx + 1 >= (int)(*totalElements) - 1) { // check if memorry is sufficient, otherwise double
*totalElements *= 2;
int* temp = realloc(*arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // just in case realloc fails
printf("Allocation error\n");
} else {
*arr = temp;
}
}
if (*lastElementIdx + 1 <= (int)(*totalElements) - 1) {
*lastElementIdx += 1; // once everything is done and if there is now enough space: add element
(*arr)[*lastElementIdx] = element;
}
}
int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
if (*lastElementIdx > -1) { // if queue is not empty...
int deleted = (*arr)[0]; // save deleted value first (in case it's still needed)
for (int i = 0; i <= *lastElementIdx; i++) { // shift all elements
(*arr)[i] = (*arr)[i + 1];
}
*lastElementIdx -= 1; // index is now decreased by 1
if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) { // cut memory in half if not needed
*totalElements /= 2;
int* temp = realloc(*arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // in case realloc fails
printf("Allocation error\n");
return 0;
} else {
*arr = temp;
}
}
return deleted;
} else { // if queue is empty, print that there's nothing to dequeue
printf("There are no elements inside the queue\n");
return 0;
}
}
void printQueue(int arr[], int lastElementIdx) {
for (int i = 0; i <= lastElementIdx; i++) { // print entire queue
printf("[%d] = %d\n", i, arr[i]);
}
printf("\n");
}
int main (void) {
size_t totalElements = 10; // number of needed elements at the time
int lastElementIdx = -1; // index of last element in queue at the time
int *arr = calloc(totalElements, sizeof(int));
int **arrpointer = &arr;
for (int i = 1; i < 101; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
for (int i = 0; i < 102; i++) {
dequeue(arrpointer, &lastElementIdx, &totalElements);
}
printQueue(arr, lastElementIdx);
for (int i = 1; i < 21; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
free(arr);
return EXIT_SUCCESS;
}

Related

How to use realloc multiple times in a program

I am confused about how to use realloc to add space for other elements in the array nums
when the program first starts it has space for two elements but if the user wants to add more elements it will cause a segfault, this means we need to create a bigger array with 3 or more elements and add them one by one for the old one until index 1 and take a user-provided integer for the third element in the array
If the program is supposed to run in a while loop which never ends unless the user kills the process means we have to use realloc every time the array gets full that said my confusion starts here
do I have to make another array that will hold the address of realloc since we need to free it later on or can it use the same pointer for multiple realloc uses
int *nums[2];
int numsSize()
{
return sizeof(nums)/sizeof(int*);
}
//return index at which user added elements end
int numsIndex()
{
for (int i = 0 ; i < numsSize(); i++)
{
if (!nums[i])
{
return i;
}
}
return numsSize();
}
void numsResize()
{
// resize nums to have space for 4 elements
}
int main(void) {
nums[0] = 10 ;
printf("Size of array : %d \n", numsSize()); // outputs 2
printf("Index of last added element in array : %d\n", numsIndex()); // outputs 1
return 0;
}
If I understand the question correctly, you need to allocate a memory dynamically.
int *nums[2]; is fixed size array of pointer, you can't grow it dynamically.
nums[0] = 10 ; is invalid statement.
DEMO
#include <stdlib.h>
#include <stdio.h>
int *ptr = 0;
int size;
int currentIndex;
void
initialize ()
{
if (ptr)
return;
size = 2;
ptr = malloc (sizeof (int) * size);
if(!ptr)
exit(0);
currentIndex = 0;
}
void
unInitialize ()
{
if (ptr)
free (ptr);
size = 0;
currentIndex = 0;
}
void
doubleTheSize ()
{
ptr = realloc (ptr, sizeof (int) * size * 2);
if(!ptr)
exit(0);
size = size * 2;
}
void
AddElement (int element)
{
if (currentIndex >= size)
doubleTheSize ();
ptr[currentIndex] = element;
++currentIndex;
}
void
printInput ()
{
for (int i = 0; i < currentIndex; ++i)
{
printf ("%d ", ptr[i]);
} printf ("\n");
}
int
main (void)
{
initialize ();
int i = 0;
do
{
printf ("Enter Element :");
scanf (" %d", &i);
//condition to break the loop
if (i == -1)
break;
AddElement (i);
printInput ();
}
while (1);
unInitialize ();
return 0;
}

C, Segmentation fault while using dynamic array in struct

I'm trying to add new element to dynamic array in C (I know that I must free all memory. I will do it later), but I get this error every time:
But, what is strange, if I compile from terminal, like that, code works properly.
So, where is the error and how i can beat it?
Thank you!
All my code:
main.c
#include <stdio.h>
#include <stdlib.h>
typedef struct vector
{
int size;
int *array;
int alreadyIn;
}vector;
vector *vectorInit(int size)
{
vector *newVec = (vector *)malloc(sizeof(vector));
if(!newVec){printf("No memory!\n"); return NULL;}
newVec->size = size;
newVec->array = (int *)malloc(size * sizeof(int));
return newVec;
}
void allocNewMemory(vector *vect, int howMuch)
{
vect->array = (int *)realloc(vect->array ,(vect->size + howMuch) * sizeof(int));
vect->size += howMuch;
}
void pushBack(vector *vect, int number)
{
int howMuch = 5;
if(vect && vect->alreadyIn < vect->size)
{
vect->array[vect->alreadyIn] = number;
vect->alreadyIn++;
}
else
{
printf("Alloc new memory for %d elements...\n", howMuch);
allocNewMemory(vect, howMuch);
pushBack(vect, number);
}
}
void printVector(vector *vect)
{
for (int i = 0; i < vect->alreadyIn; i++)
{
printf("%d ", vect->array[i]);
}
printf("\n");
}
int main()
{
int startSize = 4;
vector * vec = vectorInit(startSize);
for (int i = 0; i < 6; i++)
{
pushBack(vec, i+1);
}
printVector(vec);
return 0;
}
You never initialize the alreadyIn member in the structure. That means its value will be indeterminate (and seemingly garbage or random).
You need to explicitly initialize it to zero:
vector *vectorInit(int size)
{
vector *newVec = malloc(sizeof(vector));
if(!newVec)
{
printf("No memory!\n");
return NULL;
}
newVec->size = size;
newVec->array = malloc(size * sizeof(int));
newVec->alreadyIn = 0; // Remember to set this to zero
return newVec;
}
This problem should have been easy to detect in the debugger.
Also note that I removed the casts from malloc. One should not cast the result of malloc, or really any function returning void *.

Re allocating C array for more space

Im writing a program with a function add(a , i, n) which will add 'i' as an element to 'a', but if the array 'a' runs out of space, then I need to realloc more memory to the array. Im stuck here:
#include <stdlib.h>
#include <stdio.h>
int add(int* a, int i, int n);
int main(){
int n = 20;
int *a = (int*) malloc(n*sizeof(int));
int i;
for (i = 0; i < 100000; i++){
n = add(a, i, n);
printf("a[%d]=%d\n",i,(int)a[i]);
}
return 0;
}
int add(int *a, int i, int n){
if (i >= n){
n++;
int* b = (int*) realloc(a, n*sizeof(int));
a[i]=i;
return n;
}else{
}
}
Im not very experienced so please be gentle...
realloc tries to reallocate the given memory, but sometimes, it can't and gives you a new memory pointer.
It must be used like:
int *b;
b = realloc(a, <newsize>);
if (b) {
/* realloc succeded, `a` must no longer be used */
a = b;
/* now a can be used */
printf("ok\n");
} else {
/* realloc failed, `a` is still available, but it's size did not changed */
perror("realloc");
}
Now, you still have some trouble in your code:
The idea of function add() is to reallocate a when needed, but a is given by copy, so its value won't be changed in main.
#include <stdlib.h>
#include <stdio.h>
int add(int** a, int i, int n);
int main(){
int n = 20;
int *a = malloc(n*sizeof(int));
int i;
for (i = 0; i < 100000; i++) {
/* note how `a` is passed to `add` */
n = add(&a, i, n);
printf("a[%d]=%d\n",i,a[i]);
}
/* and finally free `a`*/
free(a);
return 0;
}
/* changed `a` type to make its new value visible in `main` */
int add(int **a, int i, int n){
if (i >= n){
/* want to write a[i], so size must be at least i+1*/
n = i+1;
/* realloc memory */
int *b = realloc(*a, n*sizeof(int));
/* check that `realloc` succeded */
if (!b) {
/* it failed!*/
perror("realloc");
exit(1);
}
/* store new memory address */
*a = b;
}
/* update memory */
(*a)[i]=i;
/* return new size */
return n;
}
Note: I removed malloc/realloc cast, see: Do I cast the result of malloc?
To have an automatically growing array in C you would normally need a helper function ensure_capacity to take care of the array reallocation.
The helper function would preferrably reallocate using 2x grow policy, so you have an amortized constant time of the append operation.
The code would look somewhatlike the below.
Note that the code is using the first 2 elements of the array to keep its capacity/size. You can use a struct of pointer + size instead, but you need to keep the two close two each other as otherwise the code won't be easy to read.
int* ensure_capacity(int* vec, int new_cap) {
if (vec == 0) {
vec = (int*) malloc(18 * sizeof(int));
vec [0] = 16;
vec [1] = 0;
} else {
int cap = vec[0];
if (cap < new_cap) {
do {
cap *= 2;
} while (cap < new_sz);
int* new_vec = (int*) realloc(vec, cap * sizeof(int));
if (new_vec != null) {
vec = new_vec;
vec[0] = cap;
} else {
// reallocation failed, handle the error
}
}
}
return vec;
}
And you would use it in your add() function like:
int* push_back(int* vec, int val) {
vec = ensure_capacity(vec, vec[1] + 1);
vec[vec[1]++] = val;
return vec;
}

Invalid assignment on dynamic array C

Hello i am making a program in C which stores integers on a dynamic array which uses realloc every time it has to add a new element, i declare the array on the main:
int *abundants;
int count = abundant_numbers(&abundants);
once finished, i want to pass the modified array to another function to make other calculations
int abundant_numbers(int *abundants[]){
if (!(*abundants = (int*) malloc(sizeof(int)))){
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
*abundants[0] = 12; //we know the very first abundant number
int count = 1, n = 14;
while (n < MAX_NUM){
if (is_abundant(n)) {
if (!(*abundants = (int*) realloc(*abundants,(count+1) * sizeof(int)))){
perror("Error in realloc\n");
exit(EXIT_FAILURE);
}
*abundants[count] = n;
count++;
}
n += 2; //no odd abundant numbers
}
return count;
}
the first time it enters on the if statement gives no problems, but the second time on the assignment i get a Segmentation Fault: 11, when accesing abundants[2], i dont understand why its not a valid position if it worked fine for abundants[1]
Thanks.
Your problem is a simple one in these lines:
*abundants[0] = 12;
*abundants[count] = n;
The indexing operator [] has higher precedence than the dereference operator *. So here you're treating your abundants as an array pointer directly and try to dereference the element. What you want instead is
(*abundants)[0] = 12;
(*abundants)[count] = n;
This should solve your problem, the remaining code will work correctly.
That being said, I would strongly suggest to use some data structure like this:
struct dynarr
{
size_t count;
size_t capacity;
int entries[];
}
and realloc() in larger chunks, always when your count reaches your capacity. realloc() is costly and you risk fragmenting your heap space in a typical heap-based implementation. Your code could look for example like this:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 1024
int is_abundant(int x) { return x; } // simple fake to make it compile, replace
struct dynarr
{
size_t count;
size_t capacity;
int entries[];
};
struct dynarr *createarr(size_t capacity)
{
struct dynarr *arr = malloc(sizeof(*arr) + capacity * sizeof(int));
if (!arr)
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
arr->count = 0;
arr->capacity = capacity;
return arr;
}
struct dynarr *expandarr(struct dynarr *arr)
{
size_t capacity = arr->capacity * 2;
struct dynarr *newarr = realloc(arr,
sizeof(*newarr) + capacity * sizeof(int));
if (!newarr)
{
perror("malloc error!\n");
free(arr);
exit(EXIT_FAILURE);
}
newarr->capacity = capacity;
return newarr;
}
struct dynarr *abundant_numbers(void){
struct dynarr *abundants = createarr(32);
abundants->entries[abundants->count++] = 12; //we know the very first abundant number
int n = 14;
while (n < MAX_NUM){
if (is_abundant(n)) {
if (abundants->count == abundants->capacity)
{
abundants = expandarr(abundants);
}
abundants->entries[abundants->count++] = n;
}
n += 2; //no odd abundant numbers
}
return abundants;
}
int main(void)
{
struct dynarr *abundants = abundant_numbers();
for (size_t i = 0; i < abundants->count; ++i)
{
printf("%d ", abundants->entries[i]);
}
free(abundants);
putchar('\n');
}
the biggest problem is that the code is expecting an array of pointers to int.
But the code is only producing an array of `int`s
And the code contains several 'magic' numbers (2, 12, 14)
int *abundants = NULL;
int count = abundant_numbers(&abundants);
int abundant_numbers(int *abundants[])
{
if (!( abundants = malloc(sizeof(int))))
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
abundants[0] = 12; //we know the very first abundant number
int count = 1;
int n = 14;
while (n < MAX_NUM)
{
if (is_abundant(n))
{
void *temp;
if (!( temp = realloc(abundants,(count+1) * sizeof(int))))
{
perror("Error in realloc\n");
free( abundants );
exit(EXIT_FAILURE);
}
// implied else, realloc successful
abundants = temp;
abundants[count] = n;
count++;
}
n += 2; //no odd abundant numbers
}
return count;
}
However, since MAX_NUM is a known value,
it would be better to just allocate that much memory in the beginning.
And strongly suggest to NOT have 'special' code
for special cases of the value of 'n'.
And give 'magic' numbers meaningful names, suggest via #define statements.
sample code follows:
#include <stdlib.h> // malloc(), free()
// use whatever value your program needs in the following statement.
#define MAX_NUM 1024
#define FIRST_ABUNDANT 12
#define STEP_AMOUNT 2
// prototypes
int abundant_numbers( int * );
int main( void )
{
int *abundants = NULL;
if (!( abundants = malloc(sizeof(int) * MAX_NUM)))
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
// implied else, malloc successful
int count = abundant_numbers( abundants );
} // end function: main
int abundant_numbers( int *abundants )
{
int count = 0;
for( int n=FIRST_ABUNDANT; n < MAX_NUM; n+=STEP_AMOUNT )
{
if (is_abundant(n))
{
abundants[count] = n;
count++;
}
}
return count;
} // end function: abundant_numbers

Freeing memory in subroutine of recursion in C

I would like to ask a question about freeing memory in C. I am implementing the mergeSort function as following:
Merge subroutine:
int* merge (int* array_left, unsigned int left_length, int* array_right, unsigned int right_length) {
unsigned int result_size = right_length + left_length;
int* result = malloc(result_size*sizeof(int));
int r = 0; // result index
// Iterate through all left and right array elements
int i = 0; // left index
int j = 0; // right index
while ( (i < left_length) && (j < right_length) ) {
if ( *(array_left+i) < *(array_right+j) ) {
*(result+r) = *(array_left+i);
i++;
} else {
*(result+r) = *(array_right+j);
j++;
}
r++;
}
// Fill the remaining elements to the result
if (i < left_length)
while (i < left_length) {
*(result+r) = *(array_left+i);
r++;
i++;
}
if (j < right_length)
while (j < right_length) {
*(result+r) = *(array_right+j);
r++;
j++;
}
return result;
}
MergeSort:
int* mergeSort(int* array, unsigned int length) {
// Base case
if (length <= 1)
return array;
// Middle element
unsigned int middle = length / 2;
int* array_right = mergeSort(array, middle);
int* array_left = mergeSort(&array[middle], length-middle);
// Result is merge from two shorted right and left array
int* result = merge(array_left, length-middle, array_right, middle);
return result;
}
The program runs correctly but I didn't free memory from my malloc calls and in fact I can't figure it out how to place free(). I tried to free array_right and array_left but I got error telling me I can only free the pointer directly allocated by malloc.
Please help! Thank you guys in advance.
You need to add
free(arrayLeft);
free(arrayRight);
and also malloc and copy the array even in case its length is one in mergeSort:
int* mergeSort(int* array, unsigned int length) {
// Base case
if (!length) return NULL;
if (length == 1) {
// Make a copy of a single-element array
int *tmp = malloc(sizeof(int));
*tmp = *array;
return tmp;
}
... // The rest of your code
}
This would ensure that the caller of mergeSort always owns the array that it gets back, and so he must free it in all cases.
The reason it didn't work when you tried it was that you did not make copies of trivial arrays, which resulted in double-freeing some of them.

Resources