0

I'm trying to learn the memory allocation in C using malloc and increasing the size of allocated array using realloc inside a function. I came across this. when I use single variable the code is working well. But when I allocate memory for second variable, it gives me weird output.

Code is below:

#include <stdio.h>
#include<stdlib.h>
#include<string.h>

# define N 3

void padd(int *a){
    int sizes = 10*sizeof(int);
    a = (void *)realloc(a,sizes);
    a[0] = 10;
    printf("inside func= %d \n",a[0]);
}

int main()
{
    int *d;
    int *pA;
    
    int size = N*sizeof(int);
    pA = (void *)malloc(size);
    //d = (int *)malloc(size);
    
    padd(pA);
    printf("outside func= %d",pA[0]);
    
}

it gives me output:

inside func= 10
outside func= 10

but if I uncomment the //d = (int *)malloc(size); line, It gives me

inside func= 10 
outside func= -1368048824

as output.

What might be wrong here?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
maney
  • 74
  • 5
  • 4
    `a` is changed only locally. The result of `realloc()` is lost after the function returns. – πάντα ῥεῖ Apr 04 '21 at 10:28
  • Ok! is there a correct way to increase the size of the pA inside the function? I'm new to C.:) – maney Apr 04 '21 at 10:31
  • Use a pointer to the pointer and assign that one, or return the pointer obtained with `realloc()` and assign it back to the original. – πάντα ῥεῖ Apr 04 '21 at 10:33
  • `realloc(a, sizes)` may return the original `a` when increasing the allocated size, but it is not required to. It also can return `NULL` on failure. Your code will only behave as you expect if `realloc()` succeeds (does not return `NULL`) AND returns the original `a` – Peter Apr 04 '21 at 10:36
  • 1
    As a general rule, you should not cast the result of `malloc` https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – KBriggs Apr 04 '21 at 22:28

3 Answers3

2

You should either get the new pointer back from padd:

int * padd(int * a){
    int sizes = 10*sizeof(int);
    a = (void *)realloc(a,sizes);
    a[0] = 10;
    printf("inside func= %d \n",a[0]);
    return a;
}
//...
int main()
{
    //...
    pA = padd(pA);
    //...
}

Or pass a pointer to the pointer:

void padd(int **pA){
    int *a = *pA;
    int sizes = 10*sizeof(int);
    a = (void *)realloc(a,sizes);
    a[0] = 10;
    printf("inside func= %d \n",a[0]);
    *pA = a;
}
//...
int main()
{
    //...
    padd(&pA);
    //...
}
mkayaalp
  • 2,631
  • 10
  • 17
2

If realloc can’t extend a buffer in place, it will allocate a new buffer, copy the contents of the old buffer to it, free the old buffer, and return the address of the new buffer (or NULL if it cannot satisfy the request); thus, the value of a can change in padd. However, that change is only applied to the formal parameter a - the actual parameter pA is not affected.

Based on the behavior, it looks like that if you allocate d, it’s allocated immediately after pA such that pA can’t be extended in place, so realloc is creating a new buffer and deallocating the old one, and the old buffer is overwritten before the printf statement in main.

You’ll want to write padd such that any changes to a are reflected in pA - either return the (potentially new) value of a or pass a pointer to pA:

void padd( int **a )
{
  size_t sizes = 10 * sizeof (int); // see note 1
  int *tmp = realloc( *a, sizes ); // see note 2
  
  if ( tmp )
  {
    *a = tmp;
    (*a)[0] = 10;
    printf( "Inside func, (*a)[0] = %d\n", (*a)[0] );
  }
  else
  {
    printf ( "Inside func, realloc failed!\n" );
  }
}

and you’d call it as

padd( &pA );

Note 1: sizeof has type size_t, not int.

Note 2: Since realloc can potentially return NULL, always assign the result to a temporary value and check it before assigning back to the original variable, otherwise you run the risk of losing access to memory you’ve already allocated. Also, the cast to void * is unnecessary and confusing since you’re assigning the result to an int * variable. Unless you’re compiling this code as C++ or under an ancient K&R C compiler, just leave the cast off completely - otherwise, your cast has to match the type of the thing you’re assigning to, which in this case is int *.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

The both your programs have undefined behavior.

The function parameter a

void padd(int *a)

is a local variable of the function that is initialized by the value of the pointer pA used as a function argument in the function call

padd(pA);

You can imagine the function definition and its call the following way

padd(pA);

//...

void padd( /* int *a */ ){
    int *a = pA;

    int sizes = 10*sizeof(int);
    a = (void *)realloc(a,sizes);
    a[0] = 10;
    printf("inside func= %d \n",a[0]);
}

So as it is seen changing the local variable a within the function padd does not influence on the value stored in the pointer pA because the function deals with a copy of the value of the pointer pA.

To change the original pointer pA within the function you need to pass it to the function by reference.

In C passing by reference means passing an object indirectly through a pointer to it. Thus dereferencing the pointer you will get a direct access to the object.

The function should be declared and defined the following way

int padd( int **a ){
    int sizes = 10*sizeof(int);

    int *tmp = realloc( *a, size );
    int success = tmp != NULL;
    
    if ( success )
    {
        *a = tmp;
        ( *a )[0] = 10;
        // or **a = 10;
        printf("inside func= %d \n", ( *a )[0] );
    }

    return success;
}

And the function can be called like

if ( padd( &pA () ) printf("outside func= %d",pA[0]);

Pay attention to that to call realloc you should use an intermediate variable because in general the function can return a null pointer. So reassigning the original pointer with a null pointer results in the original value of the pointer will be lost.

Also you should free all the dynamically allocated memory when it is not needed any more

free( pA );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335