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;
}
Related
I am implementing a function to safely realloc a structure in order not to lost the information if any allocation error occurs, something like this:
int foo (someStruct_t *ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(ptr, size);
if (tmp_ptr == NULL)
return -1;
ptr = tmp_ptr;
return 0;
}
My doubt resides in the following: Am I not duplicating the allocated memory for the structure everytime I run this function? In my line of thought I should free one of the pointers before exiting, correct?
The primary and major problem here is, after a call to foo(), in the caller, the passed argument for ptr will not be changed, as it itself is passed by value.
You need to pass a pointer to the pointer which you want to be reallocated, in case you don't want to return the new pointer.
That said, there is no "duplication" of memory here.
From realloc() point of view
realloc(), if successful, returns the pointer to the new memory and handles the job of de-allocating the older one. You don't need to worry about any duplication or memory leaks.
Quoting C11, chapter §7.22.3.5 (emphasis mine)
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. [....]
[....] If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
From the assignment point of view
A statement like ptr = tmp_ptr; does not duplicate the memory or memory contents the pointer points to, it is just having two copies of the same pointer. For example, You can pass either of them to free().
So, bottom line, to answer the "question" in the question,
In my line of thought I should free one of the pointers before exiting, correct?
No, you should not. You need to have the newly allocated pointer to be useful in the caller, free()-in it inside the called functions makes the whole function pointless. You should free the pointer from the caller, though.
int foo (someStruct_t **ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(*ptr, size);
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
}
the posted code contains several problems:
cannot change where a caller's pointer points without the address of that pointer, so use ** in the parameter list and call the function using: foo( &ptr, size);
each reference to the callers' pointer must now be dereferenced via a *
usually the size refers to the number entries room to be allocated, so size needs to be multiplied by sizeof( someStruct_t )
note the use of size_t for the size parameter because realloc() is expecting the second parameter to have the type: size_t
and now the proposed code:
#include <stdlib.h> // realloc()
int foo (someStruct_t **ptr, size_t size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc( *ptr, sizeof( someStruct_t) * size );
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
} // end function: foo
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.
From man realloc:The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails.
So in this code snippet:
ptr = (int *) malloc(sizeof(int));
ptr1 = (int *) realloc(ptr, count * sizeof(int));
if(ptr1 == NULL){ //reallocated pointer ptr1
printf("Exiting!!\n");
free(ptr);
exit(0);
}else{
free(ptr); //to deallocate the previous memory block pointed by ptr so as not to leave orphaned blocks of memory when ptr=ptr1 executes and ptr moves on to another block
ptr = ptr1; //deallocation using free has been done assuming that ptr and ptr1 do not point to the same address
}
Is it sufficient to just assume that the reallocated pointer points to a different block of memeory and not to the same block.Because if the assumption becomes false and realloc returns the address of the original memory block pointed to by ptr and then free(ptr) executes(for the reason given in the comments) then the memory block would be erased and the program would go nuts.
Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
Just don't call free() on your original ptr in the happy path. Essentially realloc() has done that for you.
ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{
printf("\nExiting!!");
free(ptr);
exit(0);
}
else
{
ptr = ptr1; // the reallocation succeeded, we can overwrite our original pointer now
}
Applying fixes as edits, based on the good comments below.
Reading this comp.lang.c question, reveals 3 cases:
"When it is able to, it simply gives you back the same pointer you handed it."
"But if it must go to some other part of memory to find enough contiguous space, it will return a different pointer (and the previous pointer value will become unusable)."
"If realloc cannot find enough space at all, it returns a null pointer, and leaves the previous region allocated."
This can be translated directly to code:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3, clean up then terminate.
free(ptr);
exit(0);
}
else if(tmp == ptr)
{
// Case 1: They point to the same place, so technically we can get away with
// doing nothing.
// Just to be safe, I'll assign NULL to tmp to avoid a dangling pointer.
tmp = NULL;
}
else
{
// Case 2: Now tmp is a different chunk of memory.
ptr = tmp;
tmp = NULL;
}
So, if you think about it, the code you posted is fine (almost). The above code simplifies to:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3.
free(ptr);
exit(0);
}
else if(ptr != tmp)
{
ptr = tmp;
}
// Eliminate dangling pointer.
tmp = NULL;
Note the extra else if(ptr != tmp), which excludes Case 1, where you wouldn't want to call free(ptr) because ptr and tmp refer to the same location. Also, just for safety, I make sure to assign NULL to tmp to avoid any dangling pointer issues while tmp is in scope.
OP: ... may be different from ptr, or NULL if the request fails.
A: Not always. NULL may be legitimately returned (not a failure), if count is 0.
OP: Is it sufficient to just assume that the reallocated pointer points to a different block of memory and not to the same block.
A: No
OP: Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
A: No.
If realloc() returns NULL (and count is not 0), the value of ptr is still valid, pointing to the un-resized data. free(ptr) or not depends on your goals.
If realloc() returns not NULL, do not free(ptr), it is all ready freed.
Example: https://codereview.stackexchange.com/questions/36662/critique-of-realloc-wrapper
#include <assert.h>
#include <stdlib.h>
int ReallocAndTest(char **Buf, size_t NewSize) {
assert(Buf);
void *NewBuf = realloc(*Buf, NewSize);
if ((NewBuf == NULL) && (NewSize > 0)) {
return 1; // return failure
}
*Buf = NewBuf;
return 0;
}
realloc will return the same address to ptr if it have enough space to extend the actual chunk of memory pointed by ptr. Otherwise, it will move the data to the new chunk and free the old chunk. You can not rely on ptr1 being different to ptr. Your program behaves undefined.
If realloc returns another address, it first deallocates the old one so you don't have to do it yourself.
By the way, never cast the return of malloc/realloc :). Your code should be like this:
ptr=malloc(sizeof(int));
ptr=realloc(ptr,count*sizeof(int));
if(ptr==NULL)
{
// error!
printf("\nExiting!!");
// no need to free, the process is exiting :)
exit(0);
}
If realloc moves your data, it will free the old pointer for you behind the scenes. I don't have a copy of the C11 standard, but it is guaranteed in the C99 standard.
You should not free your original pointer if the realloc succeeds. Whether you free that pointer if the realloc fails depends on the needs of your particular application; if you absolutely cannot continue without that additional memory, then this would be a fatal error and you would deallocate any held storage and exit. If, OTOH, you can still continue (perhaps execute a different operation and hope that memory will come available later), the you'd probably want to hold on to that memory and a attempt a another realloc later.
Chapter and verse:
7.22.3.5 The realloc function
Synopsis
1 #include <stdlib.h>
void *realloc(void *ptr, size_t size);
Description
2 The realloc function deallocates the old object pointed to by ptr 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. Any bytes in the new object beyond the size of the old object have
indeterminate values.
3 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.
Returns
4 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.
Emphasis added. Note clause 4; the returned pointer may be the same as your original pointer.
char** removeDuplicateChromosomes(char* input[], int no_of_chromosomes)
{
char** result = (char**) malloc(sizeof(char* )*(no_of_chromosomes));
//some piece of code
result[count] = input[itr];
//some piece of code . I didn't free any pointers here in this function
return result;
}
Can someone help me to identify any dangling pointers (if present) and explain the same pls?
Memory leak - if no pointer points to unfreed memory.
Dangling pointer - a pointer that points to freed memory.
Your code has a significant risk of either / both, though without a complete code sample, it's impossible to tell whether either would occur. I'll just give some possible scenario's of when it could happen.
As an initial note, If you don't free result's memory in the calling function, you'd have a memory leak. Any malloc must have a corresponding free.
If you free input like this:
free(input);
There would not be a dangling pointer.
But then there may be a memory leak if each element of input doesn't have an element of result pointing to the same memory. And if multiple elements of result point to this memory, you'll probably end up with a dangling pointer when you try to free it, along with some undefined behaviour.
If you free input like this:
int i;
for (i = 0; i < inputSize; i++)
free(input[i]);
free(input);
There would be a dangling pointer.
Why?
input[itr]; is a pointer.
result[count] = input[itr]; just makes result[count] point to the same memory as what input[itr] points to.
So if we free input[itr], result[count] would point to freed memory, and thus be dangling.
If we don't free input[itr], result[count] will still point to valid memory.
If you want result[count] to point to its own memory, you'll have to use:
result[count] = malloc(inputItrSize);
memcpy(result[count], input[itr], inputItrSize);
Note - there's no way to tell how much memory input[itr] is pointing to, so you'll have to declare inputItrSize with the appropriate size yourself.
A dangling pointer is a pointer to a memory area that is no-longer allocated.
char* dangleMeBaby(char* obj) {
free(obj);
return obj;
}
int* localDangle() {
int i = 10;
return &i;
}
#define NUM_POINTERS 8
char** wholeLottaDangle() {
char* ptr = malloc(sizeof(char*) * NUM_POINTERS);
size_t i;
void* data = malloc(NUM_POINTERS);
for (i = 0; i < NUM_POINTERS; ++i) {
ptr[i] = data + i;
}
free(data); // all the pointers in ptr now dangle.
free(ptr); // and ptr itself is now a dangle
return ptr;
}
If you have allocated input as one big block of pointers + data, then freeing input before result will turn result into an array of dangling pointers.
size_t indexSize = sizeof(char*) * numChromosomes;
size_t chromosomeSize = (MAX_CHROMOSOME_LEN) * numChromosomes;
char* data = malloc(indexSize + chromosomeSize);
char** input = (char**)data;
char* chromosome = data + indexSize;
for (size_t i = 0; i < numChromosomes; ++i, chromosome += MAX_CHROMOSOME_LEN) {
input[i] = chromosome;
}
// no dangling pointers in result until you free input.
If you have allocated chromosomes individually and then allocated "input" to house all the pointers, then freeing a chromosome without removing it from "result" will cause a dangling pointer
result = removeDuplicateChromosomes(input, 64);
free(input[0]); // result still points to it, result is dangling.
But if input and the list of chromoes remain intact until you free input() and/or any chromosomes, you have no dangling pointers.