38

From what is written here, new allocates in free store while malloc uses heap and the two terms often mean the same thing.

From what is written here, realloc may move the memory block to a new location. If free store and heap are two different memory spaces, does it mean any problem then?

Specifically I'd like to know if it is safe to use

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

If not, is there any other way to realloc memory allocated with new safely? I could allocate new area and memcpy the contents, but from what I understand realloc may use the same area if possible.

Community
  • 1
  • 1
Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
  • 14
    Just use a `vector`. – Karoly Horvath Nov 14 '15 at 08:30
  • 9
    @KarolyHorvath How do you know that's a viable approach in every case? What about deploying an embedded system without standard library support? What about integration with a C interface that may perform a realloc? – Kyle Strand Nov 14 '15 at 08:35
  • 7
    @KarolyHorvath you are welcome to benchmark how much time is takes to allocate 200 MB of memory with `malloc` (few microseconds) vs. `std::vector` (~200 millisecond!). `std::vector` is not a magic solution to every memory problem – David Haim Nov 14 '15 at 08:41
  • 2
    @DavidHaim: I could not believe that but I benchmarked it too and the problem seems to be vector's zero-initialization of the memory. `int* n = new int[200 * 1024 * 1024]{};` gives about the same performance on MSVC. – Oberon Nov 14 '15 at 08:56
  • @Oberon I didn't say `std::vector` is less performant than `new`, I compared it to `malloc`, which is a great tool when performance is importance. you can always catch a pointer handed by `malloc` whith `unique_ptr` and so, and gain that extra 200 milliseconds that you would never get over `std::vector` or `new[]` do you think it's legit to spend 200 millisecods zeroing bytes on , let's say, a web server? eventually, `std::vector` is not an automatic sulotion for C++ reallocations. many developers here said it without thinking twice without even mentioning the side affects. – David Haim Nov 14 '15 at 09:00
  • @DavidHaim I only pointed out the reason for vector's performance, I'm not saying that's good. Also, the performance of new without zero initialization (if you leave out the `{}`) is comparable to `malloc`. Additionally, you *can* use a `vector` with similar/same efficiency as `malloc` if you use `reserve` and e.g. `push_back`. – Oberon Nov 14 '15 at 09:03
  • @Oberon , yes, other than with `malloc` you can reallocate, not with `new`, and `reserve` zero the bytes as well. – David Haim Nov 14 '15 at 09:04
  • 5
    @DavidHaim: No `reserve` does not zero the bytes. You are probably confusing that with `resize`. – Oberon Nov 14 '15 at 09:06
  • Guys, what's the matter with you?? I obviously didn't say it's a magical solution or viable approach in every case. I just wrote a (probably) good suggestion. Now if for some reason the OP can't use it, I'm sure he'll mention it. – Karoly Horvath Nov 14 '15 at 10:30
  • Vector is syntactivally convenient solution, the performance issues are also good point. Thanks for all the comments, it was very enlightening to me. – Jan Turoň Nov 14 '15 at 15:15
  • 1
    @Karoly I'm sorry, but I don't feel that my response was an unfair reading of your comment. "Just use X" implies it's not even worth considering whether X is a good idea or how it compares to OP's original approach. Also, std::vector, like all classes that follow OOP principles, hides its implementation details, but it would appear that what OP is after is more control rather than less. It's also perhaps a bit insulting, since std::vector is pretty universally known, so it's unlikely that OP asked a question of this depth without considering it as an option. – Kyle Strand Nov 14 '15 at 15:33
  • 1
    Sorry to be so pedantic but since this is C++ don't you need to cast that `realloc`. I mean shouldn't you write `int* mydata = (int*)realloc(data,6*sizeof(int));` – Z boson Nov 17 '15 at 10:14

9 Answers9

56

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.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    the snippet you wrote is the most idiomatic way to reallocate memory in C++, but It's a sure way to kill your performance, If you are on this field. – David Haim Nov 14 '15 at 08:38
  • 3
    @KyleStrand: If you have to accept `new` for the original allocation, then your only recourse for efficiency is to use compiler-specific means. E.g. knowledge that `realloc` is safe with this compiler. Otherwise, you can use smart pointers with `malloc` and `realloc`. Anyway, do remember the first (and second) rule of optimization, namely to **MEASURE**. – Cheers and hth. - Alf Nov 14 '15 at 08:44
  • Using smart pointers does require using a custom deleter to invoke free() instead of delete, though, right? – Kyle Strand Nov 14 '15 at 15:42
  • 1
    @KyleStrand: With the standard library's smart pointers, yes, you need a custom deleter for built-in types. For class types a nice alternative is to redefine the type's allocation and deallocation functions (in an effort to confuse as many beginners as possible they're named respectively `operator new[]` and `operator delete[]`, and just to confuse things beyond the possible they're static but with deallocation effectively acting as if it's virtual). A third option is to define your own smart pointer from scratch, for which you might find `boost::intrusive_ptr` helpful. – Cheers and hth. - Alf Nov 14 '15 at 15:59
  • This poses an interesting dilemma for standard library authors who are implementing `std::vector`: since `realloc` would be convenient, should they use `malloc` or the more idiomatic `new`? – Nate Eldredge Nov 15 '15 at 03:56
  • @NateEldredge: There is no choice. `std::vector` uses a `std::allocator` for its memory management. – Cheers and hth. - Alf Nov 15 '15 at 04:32
  • @Cheersandhth.-Alf: But act as if they are virtual? More pedantic, they act as virtual as the dtor. – Deduplicator Nov 18 '15 at 20:40
17

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.

  • 1
    Please add references for (1) "C++'s malloc/calloc/realloc must not be implemented in terms of ::operator new", and for (2), about the in-practice not yet endorsed by standard, that "C++ compilers may e.g. combine multiple new expressions to use one single ::operator new call". – Cheers and hth. - Alf Nov 14 '15 at 08:40
  • @Cheersandhth.-Alf Added a reference for the first. Haven't included the actual standard text because this is not a [language-lawyer] question. I don't have an example ready for multiple `new` calls that gives the results I'm describing, and a quick and simple example that just deletes the allocated memory doesn't combine the allocations into one, it just optimises away the allocations entirely. –  Nov 14 '15 at 08:48
8

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.

Community
  • 1
  • 1
Paolo M
  • 12,403
  • 6
  • 52
  • 73
5

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.

Non-maskable Interrupt
  • 3,841
  • 1
  • 19
  • 26
  • 4
    I'm not sure what's inelegant about realloc. – Kyle Strand Nov 14 '15 at 08:44
  • using new/delete with realloc, by override, or other means to make it work, is not elegant, please read the topic. – Non-maskable Interrupt Nov 14 '15 at 13:36
  • So you mean that *because* it's not safe, it's inelegant to try to *make* it safe? That's not clear from your answer. And don't assume I have somehow managed to leave a comment on your answer without "reading the topic"; that's pointlessly insulting. – Kyle Strand Nov 14 '15 at 15:01
5

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.
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
4

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

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • Is that true for `operator new[]()`, which is what is used here, rather than plain `operator new()`? – Alan Stokes Nov 14 '15 at 08:41
  • on VC++ all standard `new` operators call `malloc` eventually. – David Haim Nov 14 '15 at 08:44
  • Yes, but I would be surprised if the result of `operator new[]` was the same as the value returned by a call to `malloc`, because of storing the count. And if it isn't then you can't pass it to `realloc`. – Alan Stokes Nov 14 '15 at 08:47
  • 2
    the thing that stores the count is the heap entry, and the count is bytes-count, not object count. so allocation wise, there is not difference between `new()` and `new[]`, both call `malloc` which calls `HeapAlloc`. – David Haim Nov 14 '15 at 08:49
  • 1
    That's only true if `new[]` returns the result of `malloc` directly without prepending the size of the array (which is needed for a non-trivial destructor) – ratchet freak Nov 14 '15 at 16:42
  • The "real facts" are the rules as set by the standard, not random implementation details and assumptions about the compiler and its behavior when meeting this specific flavor of UB. This is *not* safe, without any "ifs". – Baum mit Augen Jul 09 '18 at 14:12
  • @ratchetfreak No, the runtime can calculate the size of the array from the byte count and the size of the type being destroyed. – Kyle Strand Aug 14 '19 at 14:14
  • @BaummitAugen No, compiler vendors are free to provide additional guarantees beyond those required by the standard. – Kyle Strand Aug 14 '19 at 14:15
  • @KyleStrand the delete[] operator doesn't have any clean way to get the byte count of the original allocation so it has no way of knowing how many elements were allocator unless new[] adds it to the allocated memory. – ratchet freak Aug 14 '19 at 14:27
  • @ratchetfreak C permits allocating arbitrarily sized data with `malloc` and freeing it with `free`, so `malloc` _must_ include the byte count. Since both `malloc` and `new[]`/`delete[]` are implementation-provided, there does not need a "clean" way for `delete[]` to get the count; it is sufficient for it to know where it is stored. – Kyle Strand Aug 14 '19 at 14:37
  • @KyleStrand But you can provide the implementation of delete[] which means that the implementation has no choice but to add that redundant information again. – ratchet freak Aug 14 '19 at 14:59
  • @ratchetfreak You wrote that "prepending the size of the array...is *needed* for a non-trivial destructor". If you provide `delete[]`, you must also provide `new[]`, in which case you can do whatever you want. But the implementations provided by the language implementation do not have the restriction you claim. – Kyle Strand Aug 14 '19 at 16:23
  • @ratchetfreak Actually, as pointed out [here](https://stackoverflow.com/questions/197675/how-does-delete-know-the-size-of-the-operand-array#comment1483842_197699), implementations are generally not able to compute the array size from the size of the allocation. I am not sure whether this implies that implementations *can't* be designed to do such a computation, though. – Kyle Strand Aug 15 '19 at 22:27
  • @ratchetfreak I talked to an LLVM dev the other day who confirmed that indeed this is how `new[]` and `delete[]` are implemented by default. – Kyle Strand Aug 26 '19 at 18:36
4

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

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
3

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.

Community
  • 1
  • 1
codingEnthusiast
  • 3,800
  • 2
  • 25
  • 37
-2

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;