what will realloc do to the old pointer [duplicate] - c

This question already has answers here:
Using realloc to shrink the allocated memory
(5 answers)
Closed 2 years ago.
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.

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.

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.

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).

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.

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;
}

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 */
}

Related

Using 'realloc()' during runtime gives crash, why? - C Language

I have an array that I want to increase the size of during runtime. I assign values to the elements of the array by using a loop, and when the index of the loop hits the number of elements of the array, I want to increase the size of the array.
What I did, actually works; I can assign values to elements of the array that I would normally not be able to assign any values to without increasing the array's size. The bad side is, it gives me a crash after the program runs and finishes smoothly. What is wrong here? Is it that maybe the memory that I try to allocate for the array is already filled?
int main()
{
int arr[3];
int num_of_elements = sizeof(arr)/sizeof(arr[0]); // This gives '3', I checked
for(i = 0; i < 10; i++)
{
if(i == num_of_elements)
{
num_of_elements = num_of_elements + 10;
realloc(arr, num_of_elements);
}
arr[i] = i+10;
printf("%d\n", arr[i]);
}
return 0;
}
Well you are invoking undefined behavior. From standard §7.22.3.5
void *realloc(void *ptr, size_t size);
If ptr is a null pointer, the realloc function behaves like the malloc
function for the specified size. Otherwise, if ptr does not match a
pointer earlier returned by a memory management function, or if the
space has been deallocated by a call to the free or realloc function,
the behavior is undefined. If memory for the new object cannot be
allocated, the old object is not deallocated and its value is
unchanged.
By memory management function - it means malloc etc. arr is not dynamically allocated memory. So passing this to realloc is undefined behavior - in your case that behavior leads you to crash in program.
It would work if you do this
int *arr = malloc(sizeof(int)*3);
if( arr == NULL){
perror("Malloc failed");
exit(EXIT_FAILURE);
}
...
int *p = realloc(arr,num_of_elements*sizeof(int));
^^^^^
if(p == NULL ){
perror("realloc failed");
exit(EXIT_FAILURE);
}
arr = p;
Check how realloc is used.
The takeaways will be:-
Check the return value of realloc, malloc.
You were trying to reallocate extra 10 elements for which you need 10*sizeof(int) amount of memory.
Don't do arr = realloc(arr,SIZE) in case realloc fails you will have memory leak.
Why realloc to p after all you do arr=p annyway?
Two reasons so far
The answer to this is when realloc fails then it returns NULL now if you assign arr to NULL then you may have a situation where you lose the only reference to the previously allocated memory - leading to a memory leak. That's why we do it like this.
Note this from standard
The realloc function returns a pointer to the new object (which may
have the same value as a pointer to the old object), or a null pointer
if the new object could not be allocated.
Notice that may part - it might be the same address as before as pointed by arr or it might be different one. That explains why we should store it in some temporary pointer and then we assign it later.
You should have done this:
arr = realloc(arr, num_of_elements);
^^^^^
realloc() does not necessarily extend or shrink the allocated memory in-place, it invalidates the dynamic memory that its first argument points to and allocates new memory, while preserving the content of the previous memory.
One possible implementation is :
void* realloc(void* ptr, size_t size) {
void* ret = malloc(size);
if (ret == NULL) return ret;
if (ptr != NULL) {
memcpy(ret, ptr, /* previous size from system */);
free(ptr);
}
return ret;
}

Can we free dynamically allocation memory using realloc?

My friend and I had a discussion over freeing dynamically allocated memory. He told that a memory could be freed with realloc(), to which I denied.
Let's consider the below code:
int main()
{
int *p = (int *)malloc(sizeof(int) * 10);
int *q = (int *)malloc(sizeof(int) * 10);
free(p);
p = NULL;
realloc(q, sizeof(int) * 0);
q = NULL;
_getch();
return 0;
}
Let's assume p and q are pointing to address 0x1000 & 0x2000 respectively.
In the above code, p and q are int pointers pointing to dynamically allocated 40 bytes of memory blocks in the RAM. On executing free(p) frees the memory address (0x1000). The freed memory address (0x1000) could be used by an OS again. Then, NULL is assigned to pointer variable p to prevent p of becoming a dangling pointer.
realloc(q, sizeof(int) * 0);
realloc() simply shrinks the memory block pointed by q to zero. But q still points to the same address (0x2000), and that address is not freed. On assigning NULL value to pointer q, q now points to NULL and the address (0x2000) to which q was pointing is not freed and the link to that address (0x2000) is lost. That address (0x2000) cannot be used by OS in the future until the end of the program.
Is my understanding correct?
realloc(, 0) is implementation dependent and not equivalent to free().
That said, you might be lucky:
#include <stdio.h>
#include <malloc.h>
int main() {
char *a;
a = malloc(1024);
printf("a = %08lx\n", a);
free(a);
a = malloc(1024);
printf("a = %08lx\n", a);
realloc(a, 0);
a = malloc(1024);
printf("a = %08lx\n", a);
}
gcc version 6.1.1 20160815 [gcc-6-branch revision 239479] (SUSE Linux)
ldd --version
ldd (GNU libc) 2.23
a = 01dad010
a = 01dad010
a = 01dad010
So the second malloc, after realloc(, 0), "sees" a free block where before was a. I take it to mean that in this instance, realloc duplicates the behaviour of free. DO NOT RELY ON THIS.
realloc(q, sizeof(int) * 0);
is same as
realloc(q, 0);
The C standard in 7.22.3.5 ->2 The realloc function says :
..and returns a pointer to a new object that has the size specified by
size. The contents of the new object shall be the same as that of the
old object prior to deallocation, up to the lesser of the new and old
sizes.
But it doesn't explicitly specify what would happen if the new size is zero.
But you still have an artist pointer that could be passed to free.
Consider
char *a;
a=malloc(2*sizeof *a);
a=realloc(a,0);
// Note I'm purposely NOT using a temporary variable to store 'realloc' results
// Moreover, above is BAD practice.
free(a); // Is this permitted? YES !! Did we technically free memory above? NO !!
//Compiler will not yell at you for the above, in fact it is legal as per the standard
return 0; // No output for this one
vs
char *a;
a=malloc(2*sizeof *a);
free(a);
//You're not suppose to free(a) again.
free(a);
//Compiler will yell at you for the above.
return 0;
output
double free or corruption (fasttop): 0x0000000000915010
Conclusions
deallocation and freeing are not exactly the same things.
Both realloc and free can deallocate memory, but only free frees the memory.
That said
free(p);
p = NULL;
is the recommended way to free memory. Do check [ this ] answer.

realloc() not allocating after first call

I keep getting segmentation faults (I know what that means I just don't understand why I keep getting them).
int main(void)
{
srand(time(NULL));
int cardsSize = 0; //tracks the amount of memory space my pointer is pointing too.
int *cards = NULL;
if(drawCard(&cards, ++cardsSize) == -1)
{
printf("Memory allocation failed.\n");
return 0;
}
if(drawCard(&cards, ++cardsSize) == -1)
{
printf("Memory allocation failed.\n");
return 0;
}
//... more code
}
The part where realloc() gets called:
int drawCard(int **cardsptr, int cardsSize)
{
int newCard = rand() % 13;
printf("Address before:\t%p\ncardsSize:\t%d\n", *cardsptr, cardsSize); //an attempt at debugging by examining if the null pointer actually gets an address.
*cardsptr = realloc(*cardsptr, cardsSize * sizeof(int));
printf("Address after:\t%p\n", *cardsptr);
if(*cardsptr == NULL)
return -1;
if (newCard > 10)
**(cardsptr + cardsSize - 1) = 10;
else
**(cardsptr + cardsSize - 1) = newCard;
printf("Reached.\n"); //to see if I crash before or after trying to assign a value.
return 0;
}
The first time drawCard() is called everything works fine. The second time I get a segmentation fault error as soon as I want to assign a value to the newly allocated memory.
The output of the debug messages are:
Address before: (nil)
cardsSize: 1
Address after: 0x2428010
Reached.
Address before: 0x2428010
cardsSize: 2
Address after: 0x2428010
Segmentation fault (core dumped)
To my knowledge it should work.
I pass a pointer to a NULL pointer to the drawCard() function and dereference it when calling realloc(). This makes realloc() act like malloc() which also ensures that that pointer can be passed to realloc() in the future as realloc() requires a pointer that points to memory that was allocated by malloc().
I keep track of the size through cardsSize and use that in combination with sizeof to reallocate the right amount of memory. I check to see if realloc() succeeded by checking to see if it returned a NULL pointer.
Then I dereference it twice so I can put a value in one of the memory spots my initial pointer is now pointing too.
The problem is **(cardsptr + cardsSize - 1).
You add cardsSize - 1 to the pointer to the cards variable. cardsptr points to something in the stack. So adding cardsSize - 1 to it moves you to something in the stack you apparently can't dereference – and didn't intend to dereference.
So either use *(*cardsptr + cardsSize - 1) or less obscure (*cardsptr)[cardsSize - 1].
**(cardsptr + cardsSize - 1) should be changed into *(*cardsptr + cardSize - 1), or to be more intuitive, (*cardsptr)[cardSize-1].

Use of "calloc" & "realloc"

Practice
#include <stdio.h>
#include <stdlib.h>
int main(void){
int i=0,z=2;
char *p=(char *)calloc(z,(sizeof(char)));
if(!(p)){
printf("\nMemory NOT Enough\n");
goto END;
}
*p='V';
z+=2;
p=realloc(p,z*(sizeof(char))); ----A
*(p+3)='S';
for(i=0;i<z;++i)
printf("\n%d\n",p[i]);
END:free(p);p=NULL;
return 0;
}
As you can see, the line marked by A uses realloc.
In line A the p on the LHS of the = is assigned the new address generated by realloc(p,z*(sizeof(char)));.
My question is :
What happens to the previously stored address in p? Previously stored address is replaced, so does it lead to memory leak?
If the return value from realloc() is not NULL all is well;
if realloc() returns NULL you have a memory leak.
You need to use a helper variable to use realloc() safely.
char *tmp;
tmp = realloc(p, z);
if (tmp == NULL) {
fprintf(stderr, "Unable to realloc.\n");
// p still points to the old memory and its contents are valid
exit(EXIT_FAILURE); // or some other error recovery
} else {
// tmp points to a (possibly new) block of memory with the same contents
// as what p used to point to (to the maximum of the old size and z)
// p (very probably) points to an invalid address
p = tmp; // now p points to a valid address (also tmp)
// ignore tmp for now on
}
No that memory is taken care by realloc if the realloc finds sufficient memory adjascent to p then its okay else realloc moves memory to new location.note that it moves it, so old memory is freed.
realloc will attempt for the extension of the available memory range only when sufficient memory is available in the heap.
From the C standard:
size == 0
realloc might free old and return 0
alternatively realloc behaves as for size != 0 but cannot return 0
size != 0
realloc might return 0. old is not touched
if the block pointed to by old is >= size, realloc might return old
alternatively realloc allocates a block >= size, copies all bytes from old up to size, frees old and returns this new block

Understanding realloc() in C

In this example from: http://www.cplusplus.com/reference/cstdlib/realloc/
why are there two pointers: numbers and more_numbers? Can I use:
numbers = (int*) realloc (numbers, count * sizeof(int));
if (numbers!=NULL) {
numbers[count-1]=input;
}
else {
free (numbers);
puts ("Error (re)allocating memory");
exit (1);
}
The reason that example appears a little extended is to adequately deal with a failure to realloc.
numbers = (int*) realloc (numbers, count * sizeof(int));
if (numbers!=NULL) {
numbers[count-1]=input;
}
else {
free (numbers);
This will not work in the error condition; you'll be trying to free a value which you just confirmed is NULL, which probably isn't what you intend. Additionally, you've lost the (still valid) original pointer in this case (the purpose of the more_numbers pointer in your example), losing access to the data you have there and losing the ability to release the buffer.
To see why your version is wrong, you need to realise what realloc does. It does one of two things: If there is enough memory, it will return a new pointer and the old one becomes invalid. If there isn't enough memory, it will return NULL and the old pointer stays valid and unchanged.
You must store the result of realloc into a temporary variable, and not overwrite the old pointer. The reason is that if realloc returns NULL, and you overwrote the old pointer with NULL, you lost access to your existing data. So the correct way is:
int* tmp = (int*) realloc (numbers, count * sizeof(int));
if (tmp!=NULL) {
// realloc was successful. The old value of numbers is now rubbish.
// Store the result of realloc into numbers and continue.
numbers = tmp;
numbers[count-1]=input;
}
else {
// realloc failed. The old value of numbers is unchanged but doesn't have enough
// space to store input. You need to handle the error somehow.
free (numbers);
puts ("Error (re)allocating memory");
exit (1);
}
If you had stored the realloc () result directly into numbers, the "else" branch wouldn't know the old value of numbers anymore and couldn't free that memory.
According to link http://www.cplusplus.com/reference/cstdlib/realloc/
it is written "The program prompts the user for numbers until a zero character is entered. Each time a new value is introduced the memory block pointed by numbers is increased by the size of an int."
have a look at the function realloc() in http://www.tutorialspoint.com/c_standard_library/c_function_realloc.htm
void *realloc(void *ptr, size_t size);
The function returns a pointer to the newly allocated memory, or NULL if the request fails.
ptr -- This is the pointer to a memory block previously allocated with malloc, calloc or realloc to be reallocated.If this is NULL, a new block is allocated and a pointer to it is returned by the function
size -- This is the new size for the memory block, in bytes.If it is 0 and ptr points to an existing block of memory, the memory block pointed by ptr is de-allocated and a NULL pointer is returned
So after allocating memory this function returns a void pointer and it is type-casted to an integer pointer to use in the code. So each time a code is entered the memory allocated will be incremented 1 times by the sizeof(int) and the value is stored in that area.
more_numbers is the pointer which points to the starting address of the location of the memory allocated by the realloc()
numbers is the pointer which points to the array where the inputs are stored
In case if the memory allocation fails the entire memory is de-allocated using free();
You could use a function (hope i did this right):
/// int*-argument passed by-pointer
bool Enlarge(int** input, size_t new_count, int new_val)
{
void* numbers = NULL; // local ptr
numbers = (int*) realloc (*input, new_count*sizeof(int));
if (numbers!=NULL) {
*input = (int*)numbers;
(*input)[new_count - 1] = new_val;
return true;
} else {
return false;
}
}
Use it:
int* old = (int*)malloc(sizeof(int)*2);
if (!old)
return -1;
old[0] = 11; old[1] = 22;
if (!Enlarge(&old,3,33))
{
free(old);
return -2;
}
printf("A: %d, B: %d, C: %d\n", old[0], old[1], old[2]);
if (old) { free(old); }
Just beware to pass the ptr as int** and not as int* (e.g. when specifying &old[0] instead of &old.

Resources