What is C++ version of realloc(), to allocate the new buffer and copy the contents from the old one?
There's no new
/delete
equivalent of realloc
in C++.
From Bjarne Stroustrup's FAQ :
Why doesn't C++ have an equivalent to realloc()?
If you want to, you can of course use realloc(). However, realloc() is
only guaranteed to work on arrays allocated by malloc() (and similar
functions) containing objects without user-defined copy constructors.
Also, please remember that contrary to naive expectations, realloc()
occasionally does copy its argument array. In C++, a better way of
dealing with reallocation is to use a standard library container, such
as vector, and let it grow naturally.
If you want a resizeable container, just use std::vector
, otherwise stay with malloc
, realloc
and free
.
And, to answer your last question, the closest C++ version of your code would be:
main() {
std::vector<char> x(3);
x[0] = 10;
x[1] = 20;
x[2] = 30;
x.resize(4);
x[3] = 40;
for (int i = 0; i < 4; i++) std::cout << x[i] << std::endl;
}
But that is not the standard way of using a vector and provide little benefits versus simply:
main() {
std::vector<char> x;
x.push_back(10);
x.push_back(20);
x.push_back(30);
x.push_back(40);
for (int i = 0; i < 4; i++) std::cout << x[i] << std::endl;
}
Realloc equivalent in C++
You should avoid realloc
completely anyway, because you can't move around C++ objects like that.
- Use
buf = new unsigned char[sizeof(T) * capacity]
to create a new buffer - Cast the allocated
unsigned char *
toT *
and use theseT
-pointers from now on - Construct new elements via "placement
new
", as innew (&buf[i]) T(original_copy)
- To copy the buffer to a larger buffer, allocate the new one first, use
std::uninitialized_copy
(notstd::copy
), then destroy the elements in the old one usingbuf[i].~T()
and deallocate the old buffer usingdelete [] buf
.
All of this is assuming you don't have to worry about exception-safety, which is probably OK for the assignment.
Just be aware that in real-world code you'd have to guarantee exception safety and it's a lot more tedious than this.
Why is there no realloc equivalent to the new/delete family?
Realloc has two behaviors, one of them is not acceptable in the C++ object model. Realloc can increase the size of a piece of storage, or it can allocate new storage and copy everything from the old storage into the new.
The thing is, C++ doesn't think of objects as just bags of bits. They're living, breathing types that hold invariants. And some of those invariants don't tolerate having their bits copied around well.
In C++, copying an object's bits does not mean you have effectively copied the object. This is only allowed for trivially copyable types, and there are plenty of types that aren't trivially copyable.
As such, a C++ realloc equivalent cannot be used on any allocation. You would need to split the call into two separate calls: one that attempts to expand the memory and does nothing if it can't, and the regular heap allocation call into which you would manually copy using existing C++ techniques.
As one example, many std::list
implementations store a terminator node in the std::list
object itself which is used to represent the start/end of the linked list. If you simply copied its bits, pointers to the terminator node would point to the old allocation that is now gone.
That's bad.
In order to allow an object to have arbitrary class invariants that the code which accesses those types can maintain, it is necessary to treat an object as something more than just the bits of its object representation. And most C++ types maintain some invariant for which its object representation cannot survive bitwise copying.
How does realloc() reallocate the memory?
Note: All citations in the following answer are quoted from the actual C standard, ISO/IEC 9899:2018 (C18), section 7.22.3.4.
First, the synopsis for the realloc()
function from ISO/IEC 9899:2018, Section 7.22.3:
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
Despite its name, the realloc()
function does not "reallocate" anything. realloc()
is not modifying an extant object in memory. Instead, it does some sort of "create (new object) & copy the data" routine.
If size
is not 0
and ptr
either points to an object that was allocated by one of the memory management functions (not just malloc()
only) or points to NULL
, then realloc()
usually creates a new object and copies the data from the old object into the new object.
*I do say usually because you can´t assume that a new object in memory is really allocated. You must always check whether or not it was allocated by checking whether the returned pointer points to NULL
.
If the size of the new object is larger than the old object, the bytes of the new object that are beyond the size of the old object have indeterminate values. If the new object is shorter than the old object, the values inside the difference between are thrown away. Every other value remains in the new object as it was in the old one.
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.
After that, if:
ptr
is not a pointer toNULL
and is a pointer earlier returned by a memory management function, and the object this pointer is pointing to has not been deallocated before the call torealloc()
,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.
size
is not0
,If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be unchanged.
and a new object could really be allocated if
realloc()
did not return a pointer toNULL
,If size is nonzero and memory for the new object is not allocated, the old object is not deallocated
and really only if all of these premises are fulfilled, realloc()
deallocates the memory of the old object and returns a pointer with the address of the new object in memory.
The realloc function deallocates the old object pointed to by
ptr
and returns a pointer to a new object that has the size specified bysize
.
If realloc()
returns a pointer to NULL
, no new object is created and the old object remains unchanged at its address in memory.
Optionally, to make the "pseudo-reallocating" behavior almost perfect, it is possible that the new object, after the deallocation of the old object is done (if it happens), is allocated back at the same address in memory where the old object was stored.
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 has not been allocated.
In this case, there are logically two data copying processes in realloc()
, one time into a buffer object and later back to the place of where the original old object was stored. The buffer object is deallocated after the execution of realloc()
is completed.
The pointer of ptr
which first is used for pointing to the old object should not be used for the returned pointer. If the call statement to realloc()
looks like this:
ptr = realloc(ptr,size);
then you usually have a memory leak if the reallocation fails because you just overwrote the pointer to the old memory with a null pointer. If you don't have another pointer that points to it, you've leaked the memory.
Therefore, it is usually better to use a variant on:
void *new_space = realloc(ptr, new_size);
if (new_space == NULL)
{
/* …handle out of memory condition… */
/* ptr is still valid and points to the previously allocated data */
return; /* Or otherwise do not continue to the following code */
}
ptr = new_space;
size = new_size;
Note that according to what I´ve said above, the address may be the same as before the call to realloc()
.
To make sure that memory management is really happening that way, we can try this experiment:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t length1 = 4;
size_t length2 = 2;
int *ptr1 = malloc(sizeof(*ptr1) * length1);
if(ptr1 == NULL)
{
printf("The object could not be allocated!\n");
return 1;
}
printf("value (not address) of ptr1 before realloc(): %p\n", (void *)ptr1);
ptr1 = realloc(ptr1,length2);
if(ptr1 == NULL)
{
printf("No new object allocated. Old object remains!\n");
return 1;
}
printf("value (not address) of ptr1 after realloc(): %p\n", (void *)ptr1);
free(ptr1);
return 0;
}
At my try it gave the output of:
value (not address) of ptr1 before realloc(): 0x1db4010
value (not address) of ptr1 after realloc(): 0x1db4010
So, the address stored in ptr1 after the use of realloc()
is equivalent to before the call of it.
Additional Notes:
realloc()
acts asmalloc()
whenptr
is aNULL
pointer:
int *ptr = NULL;
size_t length = 4;
ptr = realloc(ptr,sizeof(*ptr) * length);
shall have the same effect as,
int *ptr;
size_t length = 4;
ptr = malloc(sizeof(*ptr) * length);
If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size.
But, in my personal opinion, you should not first allocate dynamic storage by the use of realloc()
. I recommend that you always use malloc()
or another allocating memory management function instead. It may cause some difficulties to future readers.
- You should not use
realloc(ptr,0)
as substitution forfree(ptr)
to deallocate the dynamic memory because it is implementation-defined whether the old object is really deallocated or not.
If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be unchanged.
Always use free()
to deallocate a dynamically allocated object.
Is it safe to use the realloc after the new operator in C++?
No, the behaviour is undefined. You can only call delete[]
on a pointer that you've obtained from a call to new[]
.
Using std::vector
would cause all these memory issues to fall away.
Replacing realloc (C -- C++)
C allows void*
to be implicitly converted to any pointer. C++ doesn't, so if you're using realloc
, you have to cast the result to the appropriate type.
But more importantly, using realloc
on a pointer returned by new[]
is undefined behavior. And there's no direct C++-style equivalent to realloc
.
Your choices are, from least to most idiomatic:
- Stick to
malloc
/realloc
/free
and cast the pointers. - Use
new[]
+delete[]
instead ofrealloc
- Use
std::vector<std::string>
instead of managing your own memory.
read function: copying buffer, reallocating memory
You cannot use realloc()
for statically allocated memory.
If you wan to do that, you have to use pointers and allocate the memory dynamically.
Example:
char *buf;
buf = malloc(fileSize);
Is there such a thing as 'try-reallocate' a different size on the same address?
The function std::realloc
may help you.
The reallocation is done by either:
a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined.
b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.
See also cppreference
How can I use realloc function without discarding the old memories
An example incorporating all improvements suggested in the comments and avoiding the use of global variables, and freeing all allocated memory before the program exits. There is only a need to hold the terminal window open on windows, so conditionally enclose the system("pause");
.
Putting it altogether, you would have:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *double_ptr (int *ptr, int *ptrsz) /* pass needed information as parameters */
{
int *newptr = malloc (2 * *ptrsz * sizeof *newptr); /* allocate new block of mem */
if (newptr) { /* validate allocation */
memcpy (newptr, ptr, *ptrsz * sizeof *ptr); /* copy to new block of mem */
*ptrsz *= 2; /* update allocated size */
}
return newptr; /* return pointer */
}
void print (int *ptr1, int *ptr2) /* pass needed information as parameters */
{
/* only one call to printf required */
printf ("ptr1 -> %p, *ptr1 = %d\nptr2 -> %p, *ptr2 = %d\n\n",
(void*)ptr1, *ptr1, (void*)ptr2, *ptr2);
}
int main (void) {
int *ptr1 = NULL, *ptr2 = NULL, lenPtr1 = 10; /* avoid global variables */
if (!(ptr1 = malloc (lenPtr1 * sizeof *ptr1))) { /* validate EVERY allocation */
perror ("malloc-ptr");
return 1;
}
ptr2 = ptr1; /* pointer 1 and 2 hold same address where 10 is stored in memory */
*ptr1 = 10;
printf ("lenPtr1: %d\n", lenPtr1); /* output len, addresses, values */
print (ptr1, ptr2);
if (!(ptr1 = double_ptr (ptr1, &lenPtr1))) { /* double size of ptr1 */
perror ("malloc-double-ptr1");
return 1;
}
printf ("lenPtr1: %d\n", lenPtr1); /* output len, addresses, values */
print (ptr1, ptr2);
free (ptr1); /* free allcoated memory */
free (ptr2);
#if defined (_WIN32) || defined (_WIN64)
system("pause");
#endif
}
Example Use/Output
$ ./bin/doubleptrsz
lenPtr1: 10
ptr1 -> 0xb18260, *ptr1 = 10
ptr2 -> 0xb18260, *ptr2 = 10
lenPtr1: 20
ptr1 -> 0xb186a0, *ptr1 = 10
ptr2 -> 0xb18260, *ptr2 = 10
Let me know if you have further questions.
Related Topics
How to Use Non-Default Delimiters When Reading a Text File with Std::Fstream
Profiler for Visual Studio 2008, C++
Address-Of Operator (&) VS Reference Operator(&)
Clang Doesn't See Basic Headers
Initializing Std::String from Char* Without Copy
Does a C/C++ Compiler Optimize Constant Divisions by Power-Of-Two Value into Shifts
Cmake: Failed to Run Msbuild Command: Msbuild.Exe
Reason for Using Non-Type Template Parameter Instead of Regular Parameter
Consistent Pseudo-Random Numbers Across Platforms
Difference Between 'Strcpy' and 'Strcpy_S'
How to Build Google's Protobuf in Windows Using Mingw
Why Does Wide File-Stream in C++ Narrow Written Data by Default
How to Enable Visual Styles Without a Manifest
Initializing a C++ Std::Istringstream from an in Memory Buffer
How to Declare an Array of Objects Whose Class Has No Default Constructor
Get Function Names from Call Stack
Using Boost::Iostreams::Mapped_File_Source with Std::Multimap