Is it safe to realloc memory allocated with new?

元气小坏坏 提交于 2019-11-27 11:47:53
Cheers and hth. - Alf

You can only realloc that which has been allocated via malloc (or family, like calloc).

That's because the underlying data structures that keep track of free and used areas of memory, can be quite different.

It's likely but by no means guaranteed that C++ new and C malloc use the same underlying allocator, in which case realloc could work for both. But formally that's in UB-land. And in practice it's just needlessly risky.


C++ does not offer functionality corresponding to realloc.

The closest is the automatic reallocation of (the internal buffers of) containers like std::vector.

The C++ containers suffer from being designed in a way that excludes use of realloc.


Instead of the presented code

int* data = new int[3];
//...
int* mydata = (int*)realloc(data,6*sizeof(int));

… do this:

vector<int> data( 3 );
//...
data.resize( 6 );

However, if you absolutely need the general efficiency of realloc, and if you have to accept new for the original allocation, then your only recourse for efficiency is to use compiler-specific means, knowledge that realloc is safe with this compiler.

Otherwise, if you absolutely need the general efficiency of realloc but is not forced to accept new, then you can use malloc and realloc. Using smart pointers then lets you get much of the same safety as with C++ containers.

The only possibly relevant restriction C++ adds to realloc is that C++'s malloc/calloc/realloc must not be implemented in terms of ::operator new, and its free must not be implemented in terms of ::operator delete (per C++14 [c.malloc]p3-4).

This means the guarantee you are looking for does not exist in C++. It also means, however, that you can implement ::operator new in terms of malloc. And if you do that, then in theory, ::operator new's result can be passed to realloc.

In practice, you should be concerned about the possibility that new's result does not match ::operator new's result. C++ compilers may e.g. combine multiple new expressions to use one single ::operator new call. This is something compilers already did when the standard didn't allow it, IIRC, and the standard now does allow it (per C++14 [expr.new]p10). That means that even if you go this route, you still don't have a guarantee that passing your new pointers to realloc does anything meaningful, even if it's no longer undefined behaviour.

In general, don't do that. If you are using user defined types with non-trivial initialization, in case of reallocation-copy-freeing, the destructor of your objects won't get called by realloc. The copy constructor won't be called too, when copying. This may lead to undefined behavior due to an incorrect use of object lifetime (see C++ Standard §3.8 Object lifetime, [basic.life]).

1 The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-trivial initialization. —end note ]

The lifetime of an object of type T begins when:

— storage with the proper alignment and size for type T is obtained, and

— if the object has non-trivial initialization, its initialization is complete.

The lifetime of an object of type T ends when:

— if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or

— the storage which the object occupies is reused or released.

And later (emphasis mine):

3 The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime.

So, you really don't want to use an object out of its lifetime.

It is not safe, and it's not elegant.

It might be possible to override new/delete to support the reallocation, but then you may as well consider to use the containers.

Yes - if new actually called malloc in the first place (for example, this is how VC++ new works).

No otherwise. do note that once you decide to reallocate the memory (because new called malloc), your code is compiler specific and not portable between compilers anymore.

(I know this answer may upset many developers, but I answer depends on real facts, not just idiomaticy).

That is not safe. Firstly the pointer you pass to realloc must have been obtained from malloc or realloc: http://en.cppreference.com/w/cpp/memory/c/realloc.

Secondly the result of new int [3] need not be the same as the result of the allocation function - extra space may be allocated to store the count of elements.

(And for more complex types than int, realloc wouldn't be safe since it doesn't call copy or move constructors.)

In general, no.

There are a slew of things which must hold to make it safe:

  1. Bitwise copying the type and abandoning the source must be safe.
  2. The destructor must be trivial, or you must in-place-destruct the elements you want to deallocate.
  3. Either the constructor is trivial, or you must in-place-construct the new elements.

Trivial types satisfy the above requirements.

In addition:

  1. The new[]-function must pass the request on to malloc without any change, nor do any bookkeeping on the side. You can force this by replacing global new[] and delete[], or the ones in the respective classes.
  2. The compiler must not ask for more memory in order to save the number of elements allocated, or anything else.
    There is no way to force that, though a compiler shouldn't save such information if the type has a trivial destructor as a matter of Quality of Implementation.
nikaltipar

You may be able to (not in all cases), but you shouldn't. If you need to resize your data table, you should use std::vector instead.

Details on how to use it are listed in an other SO question.

These function is mostly used in C.

memset sets the bytes in a block of memory to a specific value.

malloc allocates a block of memory.

calloc, same as malloc. Only difference is that it initializes the bytes to zero.

In C++ the preferred method to allocate memory is to use new.

C: int intArray = (int*) malloc(10 *sizeof(int)); C++: int intArray = new int[10];

C: int intArray = (int*) calloc(10 *sizeof(int)); C++: int intArray = new int10;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!