Extending and shrinking array using realloc
Asked Answered
G

3

1

I'm trying to write a program which first dynamically initialises a queue array for 100 int elements. Whenever the queue is full and another element is supposed to be queued, the original array is supposed to double it's size so new elements can be inserted. In case elements are dequeued, and the amount of elements the queue consists of falls below half of its actual size, the queue size is supposed to be cut in half. However, its size should never fall below 10.

I'm trying to expand and shrink the array with realloc, however I'm having some problems understanding its mechanics, especially when returning a new pointer. Here is my program (with some redundant printf for debugging reasons):

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <iso646.h>   



void enqueue(int arr[], int* lastElementIdx, int *length, int element);
int dequeue (int arr[], int* lastElementIdx, int *length);
void printQueue(const int arr[], int lastElementIdx);
int expandArray(int *arr, int length);
int shrinkArray(int *arr, int length, bool min);


void test1(int *arr, int* lastElementIdx, int *length)
{
    int* temp = arr;
    printf("\nprintQueue #1:\n");                   //print queue, should be empty
    printQueue(temp, *lastElementIdx);

    for(int i = 1; i <= 100; i++)                   //insert elemnts to queue
        enqueue(temp, lastElementIdx, length, i);

    printf("\nprintQueue #2:\n");                   //print queue
    printQueue(temp,*lastElementIdx);

    printf("\nAusgabe von dequeue:\n");             // dequeue array
    while(*lastElementIdx > *length/4)
        printf("\naddress: %p\tElement: %d\n", temp, dequeue(temp, lastElementIdx, length));

    free(temp);

}


void test2(int *arr, int* lastElementIdx, int *length)
{
    int *temp = arr;
    printf("\n************\nEnqueue beyond queue[N-1]:\n");
    puts("Queue aufbauen...");
    for(int i = 1; i <= 150; i++)
        enqueue(temp, lastElementIdx, length, i);

    printf("\nprintQueue:\n");
    printQueue(temp,*lastElementIdx);

    printf("\nDequeue:\n");
    while(*lastElementIdx > *length/4)
        printf("\naddress: %p\tElement: %d\n", temp, dequeue(temp, lastElementIdx, length));

    free(temp);

}



int main(int argc, char const *argv[])
{
    int startingPoint = -1, *lastElementIdx = &startingPoint, N = 100, *length = &N;
    int *queue = (int*) calloc(*length, sizeof(*queue));

    test2(queue, lastElementIdx, length);
    queue = (int*) calloc(*length, sizeof(*queue));
    test1(queue, lastElementIdx, length);


    return 0;
}

int expandArray(int *arr, int length)
{
    /*function to double the array size*/

    length *= 2;
    int *temp;
    temp = (int*) realloc(arr, sizeof(*arr)*length);
    if (!temp) {
        free(temp);
    }
    else{
        if (arr != temp) {
            free(arr);
            arr = temp;
        }
        else{
            arr = temp;
        }
    }

    printf("EXPAND ARRAY: %p\n", arr);
    return length;
}

int shrinkArray(int *arr, int length, bool min)
{
    /*function that cuts array in half*/
    int *temp;

    if (min){
        length = 10;
    }
    else{
        length /= 2;
    }

    temp = (int*) realloc(arr,sizeof(*arr)*length);
    if (!temp) {
        free(temp);
    }
    else{
        arr = temp;
    }

    printf("SHRINK ARRAY: %p\n",arr);
    return length;
}

void enqueue(int arr[], int* lastElementIdx, int *length, int element)
{
    if ( *lastElementIdx < *length - 1){        //checks if there's space for another element
        arr[*lastElementIdx + 1] = element;     //if yes, insert element after lastElementIdx
        (*lastElementIdx)++;                    //increment lastElementIdx
    }
    else{
        *length = expandArray(arr, *length);    //if not, expand array
    }
}



int dequeue (int arr[], int* lastElementIdx, int *length)
{
    printf("address before:\t%p\tLast Element: %d\tLength: %d\n", arr,*lastElementIdx, *length);
    int *p = arr;
    if(*lastElementIdx > -1){               //Checks if there is an element in the queue
        if (*lastElementIdx + 2 < *length/2 and *lastElementIdx + 2 > 10) {
            bool min = false;
            *length = shrinkArray(arr, *length, min);
        }
        else if (*lastElementIdx + 2 < 10){
            bool min = true;
            *length = shrinkArray(arr, *length, min);
        }

        (*lastElementIdx)--;        //shift position of last element
        printf("address afterw:\t%p\tLast Element: %d\tLength: %d\n", arr, *lastElementIdx,*length);
        return *(p + *lastElementIdx + 1);
    }


    return 0;
}



void printQueue(const int arr[], int lastElementIdx)
{
    while( lastElementIdx > -1){
        printf("%d\t", *(arr + lastElementIdx));
        lastElementIdx--;   
    }   
}

However I keep getting 2 errors. The first one is here:

if (arr != temp) {
            free(arr);
            arr = temp;
        }

saying error for object 0x1001013b0: pointer being freed was not allocated. I implemented this line in the first place, because after a while I figured out the pointer address of the reallocated memory changes sometimes. If I delete that if statement I still keep getting an error, this time being in this line:

temp = (int*) realloc(arr, sizeof(*arr)*length);

the message being: malloc: *** error for object 0x100500000: pointer being realloc'd was not allocated

I should also add, that in both cases, sometimes the program is executed without any problems. In the latter case, the error is alternating between the realloc line in expandArray and the one in shrinkArray. I don't really understand why this happens, and how to handle the situation when realloc returns a new pointer address. Inspired by similar posts, I tried different approaches, like passing int **arr instead of int *arr to both expandArray and shrinkArray. I also tried different methods to free the original array after realloc like

temp = (int*) realloc(arr,sizeof(*arr)*length);
if (!temp) {
    free(temp);
}
else{
    free(arr);
    arr = temp;
}

always with the same error message. And I hoped freeing the memory after each test function and allocating new memory in the main function for the queue array before calling the second test functions would solve the problem, when in fact it didn't.

I really appreciate any kind of help on this.

Gourami answered 5/1, 2016 at 15:30 Comment(3)
Comment: it is pretty unusual to use the <iso646.h> header. Eyeballing the code, I spotted one instance of and in place of &&. I wonder if that's really an advantage. It isn't technically wrong; it is unusual, though.Jess
Ok thanks. Actually I was more just playing with it, since I read about it a few days ago. This was also the first and only time I used it.Gourami
Note: 1) realloc() may return NULL in cases other than out-of-memory as in realloc(arr,sizeof(*arr)*length);, if length == 0. So rather than if (!temp) {free(temp); use if (!temp && length != 0) {free(temp); Better yet, detect if (length <= 0) beforehand and simply call free(arr). 2) Better to use size_t length.Impunity
T
5

realloc does not work as you expected. realloc does the job for you. It takes a pointer to a dynamic memory changes size of dynamic memory an returns the pointer, for this possibly new memory is allocated, data in memory is moved and old memory is freed.

Adapt your code like this:

int expandArray(int **arr, int length)
                 // ^^ pointer to array input and output
{
    int newLength = length * 2;
    int *temp = realloc( *arr, sizeof(int) * newLength); 
    // If the function fails to allocate the requested block of memory,
    // a null pointer is returned,
    // and the memory block pointed to by argument ptr is not deallocated
    if ( temp != NULL ) {
        *arr = temp;
        length = newLength;
    }

    printf("EXPAND ARRAY: %p\n", *arr);
    return length;
}

int shrinkArray(int **arr, int length, int min)
                 // ^^ pointer to array input and output 
{
    int newLength;
    if (min){
        newLength= 10;
    }
    else{
        newLength = length / 2;
    }

    int *temp = realloc( *arr, sizeof(int) * newLength ); 
    // If the function fails to allocate the requested block of memory,
    // a null pointer is returned,
    // and the memory block pointed to by argument ptr is not deallocated
    if ( temp != NULL ) {
       *arr = temp;
        length = newLength;
    }

    printf("SHRINK ARRAY: %p\n",*arr);
    return length;
}

Finally you have to adapt functions enqueue and dequeue too, similar to expandArray and shrinkArray.

void enqueue(int **arr, int* lastElementIdx, int *length, int element);
              // ^^
int dequeue (int **arr, int* lastElementIdx, int *length);
              // ^^

void enqueue(int **arr, int* lastElementIdx, int *length, int element)
{
    if ( *lastElementIdx < *length - 1){        // checks if there's space for another element
        (*arr)[*lastElementIdx + 1] = element;  // if yes, insert element after lastElementIdx
        (*lastElementIdx)++;                    // increment lastElementIdx
    }
    else{
        *length = expandArray(arr, *length);    // if not, expand array
    }
}

int dequeue (int **arr, int* lastElementIdx, int *length)
{
    printf("address before:\t%p\tLast Element: %d\tLength: %d\n", arr,*lastElementIdx, *length);
    if( *lastElementIdx > -1 ){   // Checks if there is an element in the queue
        if ( *lastElementIdx + 2 < *length/2 && *lastElementIdx + 2 > 10) {
            *length = shrinkArray( arr, *length, 0 );
        }
        else if ( *lastElementIdx + 2 < 10 ){
            *length = shrinkArray( arr, *length, 1 );
        }
        (*lastElementIdx)--;   // shift position of last element
        printf("address afterw:\t%p\tLast Element: %d\tLength: %d\n", *arr, *lastElementIdx,*length);
        return *(*arr + *lastElementIdx + 1);
    }
    return 0;
}
Totalitarian answered 5/1, 2016 at 15:45 Comment(3)
Thanks. I've been adapting my code to yours. I still keep getting the same issue. Even with the previous versions, before you edited it. It still sometimes runs without an error, but most of the times it tells me pointer being realloc'd was not allocated. I'll keep trying, maybe there's something wrong with my other functions which call these two functions.Gourami
Hm, no. According to the output the length never falls below 10. Also the error during shrinkArray realloc always occurs at its first call, more precisely in this case in the moment of shrinking from 200 too 100. If the program succeeds in it, it succeeds with all the other remaining decreases as well. The first test function is terminated, the second one is called. But here it then stops working as soon as expandArray is called. Again right at the first call. So it seems like the first realloc call in each function is crucialGourami
YES!!! I love you man! I was working on it until I saw your comment. The code works perfectly now. I've been sitting the last 2 days on it, trying to figure out a way through this by myself, before I finally gave up and decided to post it here. You, sir, made my day!Gourami
S
2
int foo(int *arr, ... ) {
  arr = ...
}

arr is a function parameter. Thus is just a copy of the one in the caller. Changing it like that inside the function changes only the function argument. The caller retains the old value.

You need:

int foo(int **arr, ... ) {
  *arr = ...
}

Congratulations. You have just unlocked the 2 star programmer badge.

Singleton answered 5/1, 2016 at 15:35 Comment(3)
And how then works memset for example? it's first argument is void *, and not void **.Ideomotor
@Ideomotor memset doesn't change the value of the pointer. It changes the contents of the memory the pointer points to.Singleton
@reinka: To understand '2 star programmer' badge, you also need to understand that Three Star Programmer is not a term of praise, in general (unlike being a 'three star general', but they're usually not programmers). Using two stars in C is perfectly acceptable and sometimes necessary, as here. Three stars is more often an indication of probable problems.Jess
B
0
 #include<stdio.h>
 #include<stdlib.h>
 int main(){
 int *a,*b,*a1;
 int n,i,k=0;
 scanf("%d",&n);
 a=(int *)malloc(n*sizeof(int));
 b=(int *)malloc(n*sizeof(int));
printf("Enter a");
for(i=0;i<n;i++){
    scanf("%d",&a[i]);
}
printf("Enter b");
for(i=0;i<n;i++){
    scanf("%d",&b[i]);
}
a1=(int*)realloc(a,(2*n)*sizeof(int));
for(i=n;i<(2*n);i++)
{
    a1[i]=b[k];
    k++;
}

for(i=0;i<(2*n);i++)
    printf("%d",a1[i]);
}
Buddy answered 17/7, 2018 at 8:35 Comment(2)
Hey! @nitin, please do try to explain your answer instead of just pasting code. :)Bobodioulasso
Hey @surajsn thanks for suggestion bro...So basically in my code,im merging 2 arrays in O(n) using realloc().Buddy

© 2022 - 2024 — McMap. All rights reserved.