what will realloc do to the old pointer [duplicate]
Asked Answered
G

6

5

I have a question about the realloc function. Will the content of old pointer be changed after apply realloc function? The code is

main () {
    int *a, *b, i;

    a = calloc(5, sizeof(int));
    for (i = 0; i < 5; i++)
            a[i] = 1;
    for (i = 0; i < 5; i++)
            printf("%d", a[i]);
    printf("\n%p\n", a);

    b = realloc(a, 200000 * sizeof(int));
    if(b == NULL)
            printf("error\n");
    for (i = 0; i < 5; i++)
            printf("%d", a[i]);
    printf("\n");
    for (i = 0; i < 10; i++)
            printf("%d", b[i]);

    printf("\n%p %p\n", a, b);
}

The output is

11111
0x2558010
00111
1111100000
0x2558010 0x7f29627e6010

Pointer a still point to the same address, but the content is changed.

Gyromagnetic answered 28/4, 2016 at 19:3 Comment(7)
Read realloc() specification and perhaps the Linux manual or any manual. The behavior is not really consistent, it depends. Also, don't do main() like that without int return type, that's is really old and deprecated c.Warrigal
C is strictly pass by value! How would realloc even be able to change the pointer?Dilley
@iharob: I which sense is it not consistent? The C spec is very clear about the behaviour.Dilley
@Olaf it could return a different pointer or the same one if it were possible, that's what I mean. Am I wrong?Warrigal
@iharob: No, you are not, but that is the behaviour defined in the standard, so it is not inconsistent, but normal. As much as fread may return less data than requested, etc.Dilley
It's inconsistent in the sense that sometimes it's one and another time the other. As the "behavior" it is of course consistent with the spec.Warrigal
This ia the part where you should think twice before you use realloc. The main risk is when there are more pointers involved when they point to the same pointer which was malloced. They will loose the track if the pointer is reallocedCleon
M
11

Pointer a still point to the same address, but the content is changed.

That's because realloc() may first try to increase the size of the block that a points to. However, it can instead allocate a new block, copy the data (or as much of the data as will fit) to the new block, and free the old block. You really shouldn't use a after calling b = realloc(a, 200000 * sizeof(int)) since the realloc call may move the block to a new location, leaving a pointing to memory that is no longer allocated. Use b instead.

Mosenthal answered 28/4, 2016 at 19:16 Comment(1)
And since the new pointer to a different block must be allocated before realloc frees the old memory (the data has to be copied), the OP example may well still be able to access the previous data - but that is undefined behaviour.Gemeinschaft
R
6

The value returned by realloc tells you whether it succeeded or failed.

b = realloc(a, 200000 * sizeof(int));

If it fails, it returns a null pointer, and a still points to the original unmodified chunk of memory (and of course b is a null pointer).

If it succeeds, then b points to a (possibly newly allocated) chunk of memory, and the value of a is indeterminate. If it was able to allocate the new chunk in the same place as the old one (by growing or shrinking the chunk in place), then b will be equal to a -- but testing that, or even referring to the value of a, has undefined behavior. If it has to relocate the chunk, then realloc will have done the equivalent of free(a) after copying the data. In either case, it's probably best to set a to NULL to avoid accidentally referring to its (now indeterminate) value.

Note that realloc can relocate chunk even if the new size is smaller.

Rhoads answered 28/4, 2016 at 21:12 Comment(1)
Realloc definitely frees the original pointer if it was successful? Do you have documentation for that? I believe you, but I'd like to have evidence in case someone asks.Uncomfortable
F
1

A simple realloc implementation should answer your questions:

void * realloc(void * ptr, size_t desired_size) {
    size_t allocated_size = _allocated_size_of(ptr);
    if (allocated_size < desired_size) {
        void * new_ptr = malloc(desired_size);
        memcpy(new_ptr, ptr, allocated_size);
        free(ptr);
        ptr = new_ptr;
    }
    return ptr;
}

malloc and related functions don't always allocate exactly the desired size. Very often they allocate more than the desired size. There is some hidden data kept up with by the memory allocation functions which allows for a pointer that was allocated by malloc or related functions to be used to look up the memory block size that was allocated. How this is kept up with isn't necessary to understand, but some very simple implementations simply store the size in the space just before the pointer returned *(((size_t)ptr)-1).

Flagstaff answered 28/4, 2016 at 19:23 Comment(3)
I thought realloc can shrink memory too.Gemeinschaft
Perhaps this SO post can contribute to the topic - here's the linkHyper
@WeatherVane: It can, but in this simplified version the actual allocated memory block size isn't ever decreased. Trying to do that in this code would have made it much less concise and would have started to depend too heavily on the memory allocation implementation. If this isn't sufficient for the question, my apologies.Flagstaff
M
0

Reading the man page is key here, but the TLDR is if there isn't enough memory to enlarge at the back end of the previous block, it will get a new block of memory, copy the old data into it, and return the address of the new block. The old address should not be used, and most typical realloc statement looks like this

   a = realloc(a, 200000 * sizeof(int));

That way you won't accidentally use the possibly wrong old value.

It can't change the address in the pointer, since it is passed by value, so changing it in the function is only changing the local copy.

EDIT : Per Weather Vane's absolutely correct comment, the safer route would be

   void * b = realloc(a, 200000 * sizeof(int));
   if ( b ) {
       a = b;
   } else {
       ;;; /* error handler here */
   }
Mcgovern answered 28/4, 2016 at 19:14 Comment(1)
That's against the usual advice, which is to assign the new pointer to a different variable. Then if it is NULL the previous pointer is still valid, and any serious app that doesn't just exit at that point can implement a recovery strategy - for example to file important data that you really don't want to lose. If the allocation was good, you replace the original pointer.Gemeinschaft
H
0

If realloc() returns a pointer different from the one you passed in (as it will most of the time), then the pointer you passed in no longer belongs to you, and you have no business knowing or caring what becomes of it. It might change its contents, it might not. But you are no longer allowed to access it, so it can be no concern of yours.

Hopping answered 28/4, 2016 at 19:51 Comment(0)
D
0

If 'a' points a valid block of memory (from a previous malloc/realloc/calloc), then a realloc call will attempt to provide a block of memory with the new size you requested
The realloc call should be of the form *tmp = realloc (a ...

The return value from realloc must be tested
If it is NULL, realloc was unable to allocate the requested memory, and this leaves 'a' as a valid pointer
You are then responsible for handling any data pointed to by 'a' (save it / discard it) and you are responsible for free ing the memory pointed to by 'a'

If the realloc call was successful make b = tmp and now 'b' is the new pointer to the block of memory - it does not matter whether the start location is the same as 'a' or different. 'a' is no longer a valid memory allocation pointer, although further errors will depend on whether 'a' points to memory owned by your program or not - basically if a == b, 'a' can be accessed without obvious errors.

After a valid *tmp = realloc(a ... & b = tmp;:
1) If the start location of the reallocated memory was unchanged: (a == b)
it will allocate the requested memory
but run it under valgrind and you will see error messages:
Invalid free() / delete / delete[] / realloc()
Address 0x51fc040 is 0 bytes inside a block of size 256 free'd
In this case realloc could not free the memory pointed to by 'a'
and again in this case 'a' can still be accessed as it is a pointer to memory that is allocated to your progam

2) If the start location of the reallocated memory was changed: (a != b)
it will fail and Valgrind shows output like this:
address of a: 0x1e89010
address of b: 0x7f2c5893c010
a after realloc: 0x1e89010
Error in `./test15': realloc(): invalid old size: 0x0000000001e89010

and trying to access 'a' will fail - even trying to print it's value as a pointer fails, presumably because it no longer points to memory owned by the program

In other words, using 'a' after b = realloc(a ... is undefined behaviour.
The above commentary was based on using the following code:

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

int main(void)
{
    int *a = NULL, *b = NULL, *c = NULL;

    /* initial allocation */
    a = malloc(256);
    if( a == NULL) return (1);
    printf("address of a: %p\n", a);

    /* reallocation 'b' MAY be same as 'a' - try much larger allocations */
    void *tmp = realloc(a, 512);
    if ( !tmp ) {
        free(a);
        return (1);
    } else {
        b = tmp;
    }
    printf("address of b: %p\n", b);

    /* see what 'a' is now - this MAY crash the program*/
    printf("a after realloc: %p\n", a);

    /* 'a' may not be a valid pointer - try using it for another realloc */
    c = realloc(a, 256);
    /* Valgrind shows that memory could not be free'd or 'a' was not valid allocated memory */
    printf("return value of c: %p\n", c);
    if (c != NULL) {
        free(c);
        printf("'c' allocated\n");
    } else {
        free(b);
        printf("'c' not allocated\n");
    }

    return 0;
}
Dotty answered 29/4, 2016 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.