I am trying to use realloc since i want to improve speed in my code. When a certain condition is fulfilled, I want to realloc a void double pointer to a larger size, but I get a segmentation fault. Here is the code.
if (p_bheap->currentSize == p_bheap->arraySize){
p_bheap->arraySize = p_bheap->arraySize*2 + 1;
p_bheap->pp_array = realloc(p_bheap->pp_array, p_bheap->arraySize);
}
This however results in a segmentation fault. If I however make my own reallocating function it works.
if (p_bheap->currentSize == p_bheap->arraySize){
p_bheap->pp_array = bheap_reallocate(p_bheap);
}
void** bheap_reallocate(bheap* p_bheap){
p_bheap->arraySize = p_bheap->arraySize*2 + 1;
void** pp_newArray = malloc(p_bheap->arraySize*sizeof(void*));
for (int i = 0; i < p_bheap->currentSize; i++){
pp_newArray[i] = p_bheap->pp_array[i];
}
free(p_bheap->pp_array);
return pp_newArray;
}
Is there any clear errors that can be spotted that I have failed to see? And for those wondering I am programming a binary heap.
If you have a previously allocated object, say str and want to adjust its memory to another size, use a temporary variable to prevent memory loss in the event realloc fails. Illustration:
char *str = calloc(20, 1);
if(str) // should always check return of calls to [c][m][re]alloc.
{ // at this point, str owns 20 bytes of memory. If you use str in a subsequent
// call to realloc, and the call fails, then the memory previously allocated in
// the original call to calloc() will be lost. (memory leak).
....
char *tmp = {0};//So instead, use a temporary variable to accept memory
//and provide a way to recover if allocation fails
tmp = realloc(str, 40);//newsize (40) in bytes
if(!tmp) //test reallocation
{
//reallocation failed.
//free previously allocated memory, and decide how to proceed.
//In this case, return a NULL, and let the calling function decide.
free(str);//if allocation fails, free the previous object and leave
return NULL;
}
//reallocation succeeded. Proceed normally
str = tmp;//reallocation succeeded, assign its address to str and proceed normally
You can't use the same memory space to receive the result. Try to use a temporary array or to allocate another space.
Related
Many people recommend doing the following to reallocate memory:
int *temp=realloc(previousVar,newSize);
if(temp==NULL){
printf("error\n");
exit(-1);
}
previousVar=temp;
I understand that, if no new variable was created, the previous pointer would be lost, as NULL would be assigned to it. But what's the point on having all this trouble, if the program will exit in case of failure anyways?
Here's a stack overflow, where the top answer has what I'm referring to:
Proper usage of realloc()
In the example that you provided it really does not matter if you assign the return value to a temporary or not. In the linked answer, the author frees the original buffer, but one could argue that it is not necessary since the process will exit anyway.
However, consider the following example:
typedef struct
{
int *values;
size_t capacity;
size_t size;
} MyBuffer;
bool append(MyBuffer *buffer, int value)
{
if (buffer->size == buffer->capacity)
{
// No more space, double the buffer.
size_t new_capacity = buffer->capacity * 2;
int *temp = realloc(buffer->values, new_capacity);
if (temp == NULL)
{
return false;
}
buffer->values = temp;
buffer->capacity = new_capacity;
}
buffer->values[buffer->size] = value;
buffer->size++;
return true;
}
The function append tries to add a new value to an already allocated buffer. If the buffer is not large enough, it tries to use realloc to obtain a larger buffer. If realloc fails the function returns false.
If we change the realloc call to set directly buffer->values we will leak memory in case of failure:
buffer->values = realloc(buffer->values, new_capacity);
if (buffer->values == NULL)
{
// Here the original `buffer->values` is lost and we can never access it again.
return false;
}
realloc does not free the original buffer, so in case of failure we no longer have access to it. This is a problem: in the best case scenario it is a memory leak, in the worst case we may have lost some data that we still needed (on top of the memory leak).
So this depends on how you handle realloc failures. If you log an error and exit immediately you don't need to use another variable to store the result. If you report the error up the call chain, or there is some cleanup that you want to do you have to do this. If you take the first approach you need to be careful if you refactor that piece of code to return an error instead of exiting, so there is some merit to always assigning the result to another variable.
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;
}
int main()
{
int *ptr = (int*)calloc(10,sizeof(int));//allocating memory for 10 integers
ptr = realloc(ptr,20*sizeof(int)); //reallocating the memory for 20 integers
free(ptr);
return 0;
}
Initially the ptr hold memory contains zeros,but the newly created memory contains zeros or garbage values.
If zeros present how can realloc know weather the ptr is created using malloc or calloc.
Even if you called realloc properly like (without casting the result and assigning it back or it doesn't work properly):
ptr = realloc(ptr,20*sizeof(int));
(and some may say that it's not safe as realloc can return NULL thus losing the ref to ptr)
it doesn't. It just reallocates without setting the rest to 0
You have to set the rest of the memory to 0 manually using memset for instance.
I would do:
int *ptr_new = realloc(ptr,20*sizeof(int));
if (ptr_new == NULL) { /* print error, free(ptr) and exit: no more memory */ }
else
{
// set the end of memory to 0
memset(ptr_new+10,0,sizeof(int)*10);
...
Note: a common mistake is not assigning back the result of realloc since it seems to work, up to the point where the OS needs to move the memory to another block, in that case your ptr pointer becomes invalid and you have undefined behaviour.
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 */
}
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.