0

Using the std::realloc function:

If the new size is smaller, does it always have warranty to keep the memory block on the same position and only make it smaller, or it can move sometimes the whole block?

The reason to ask this, is that we'are writing a large and very hard code, and it is useful to make read only all the variables we need to leave unchanged, to obtain compiler's errors, when we try to change the wrong variable.

#include<cstdlib>
#include<iostream>
using namespace std;

int main(){
  //From 10,000,000 unsigned ints to 10 unsigned ints
  unsigned int * const array=new unsigned int[10000000];
  cout<<array<<endl;
  realloc(array,10*sizeof(unsigned int));
  cout<<array<<endl;
  delete array;
  return 0;
}
ncomputers
  • 3,680
  • 1
  • 15
  • 16
  • 5
    You shouldn't mix `new()` and `realloc()`. – πάντα ῥεῖ Sep 17 '14 at 19:44
  • 3
    You can't realloc memory that was allocated with `new`. – AnT stands with Russia Sep 17 '14 at 19:46
  • Which C library are you using? (realloc coming from the C library) – Sam van Kampen Sep 17 '14 at 19:54
  • You can't mix `new` with `realloc`. So instead of this question and this one from you: http://stackoverflow.com/questions/25898160/c-make-dynamic-array-smaller, write your own version of `realloc` that respects operator `new`. Then you won't have any questions because you would control exactly how your home-made `realloc` function behaves. – PaulMcKenzie Sep 17 '14 at 19:59
  • `realloc` might move the allocated chunk of memory, even if it shrinks, to improve future allocations. For example, large allocations might come from one region of memory and small allocations from another. – Keith Thompson Sep 17 '14 at 20:34

4 Answers4

3

No!! If realloc succeeds, the old pointer (unless it was a nullpointer) is indeterminate.

Also, do not mix incompatible memory-management-functions (assume incompatibility unless guaranteed otherwise).

realloc only has the guarantees explicitly given in the standard:

  • If return-value is non-0: New pointer points to of at least size byte, the first min(oldsize, newsize) being equal to the passed block.
  • Else if size is non-0, nothing happened to the passed block.
  • Else the old block may have been deallocated, or not.

Moral: Never pass a 0 size to realloc, and only use the old pointer for anything (including comparison to the new pointer), if realloc failed (or you passed a nullpointer).

7.22.3.5 The realloc function

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

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.

nobody
  • 19,814
  • 17
  • 56
  • 77
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • The OP is not passing 0 to realloc, so most of your answer is completely redundant in answering his question. Sound familiar? – Jonathan Potter Sep 17 '14 at 20:11
  • Well, now you are reaching. I'm giving the complete picture, which is somewhat different from decrying his example for illustrating his question. – Deduplicator Sep 17 '14 at 20:17
  • Pointing out the adverse effects (memory leak, accessing deallocated memory) of using a function incorrectly is not "decrying his example", it is attempting to illustrate why he should not use the method he is trying to use. Get a grip. – Jonathan Potter Sep 17 '14 at 20:19
  • Well, my last try to make you see the point (or as you put it, to let you "get a grip"): His example illustrates his question whether he can assume the new pointer being identical to the old pointer by assuming so, and you don't see that saying No to the question is identical to saying that the assumption the example is built on does not hold? Some people... – Deduplicator Sep 17 '14 at 20:30
  • My last try for you: He said "can I do X?". I replied "No you can't do X, because here's what will go wrong if you do". You replied "no you can't do Y". As you said, some people. – Jonathan Potter Sep 17 '14 at 20:32
3

Although I agree with the other answers in that you should not depend on it, there is an answer to be found in the glibc source. (I am assuming that you are using glibc, as you have not (yet) answered my comment asking which C library you are using)

EDIT: Using realloc on memory allocated by new is indeed disallowed, as other answers have mentioned.

Memory allocated without internally using mmap

If a block of memory is not allocated with mmap, __libc_realloc calls the _int_realloc function, which contains the following snippet of code:

if ((unsigned long) (oldsize) >= (unsigned long) (nb))
 {
   /* already big enough; split below */
   newp = oldp;
   newsize = oldsize;
 }

This makes the pointer to the new memory equal the pointer to the old memory and sets the size accordingly. Note the split below comment; the old memory block may be resized to the requested size, but is not moved.


Memory allocated internally using mmap

If the memory was internally allocated using mmap, there are two ways of resizing the memory area; mremap_chunk or a series of calls to malloc, memcpy and free. If the mremap_chunk function is available, it is used instead of the latter option.

Memory reallocated using mremap_chunk

The function mremap_chunk contains this snippet of code

/* No need to remap if the number of pages does not change.  */
if (size + offset == new_size)
    return p;

If the number of pages does not change from the old size to the new size, there is no need to remap and the old pointer is returned.

Memory reallocated using malloc, memcpy and free

If mremap_chunk is not available, the __libc_realloc source continues with the following:

/* Note the extra SIZE_SZ overhead. */
if (oldsize - SIZE_SZ >= nb)
    return oldmem;                         /* do nothing */

If the oldsize variable minus the chunk size is more than or equal to the new size, just return the old memory.


Well then, here we are. In all cases, glibc returns a pointer to the old memory, not moving it (but possibly resizing it). If you are using glibc (and can somehow guarantee that the only C library you are using it with is glibc, and can guarantee that it won't change at some point in the future), you are able to rely on the behavior that realloc does not move a block of memory if the requested size is equal to or less than the old size.

Community
  • 1
  • 1
Sam van Kampen
  • 973
  • 9
  • 17
  • 1
    Very implementation-dependent and volatile information. Be aware that even if the returned pointer is actually the same as the passed pointer, all variables heretofore storing the old pointer are indeterminate afterwards. This is not a theoretical point, but was demonstrated in the wild, using clang, in a blog post I recently read on UB. I'm just sorry that I lost the link... – Deduplicator Sep 17 '14 at 20:23
  • 1
    That seems weird- a shame you've lost the link, seems like an interesting read. But yes, I agree with you; this was more of an interest in how `glibc` handled this than very portable or practical information. – Sam van Kampen Sep 17 '14 at 20:38
  • Your answer is very good. Thank you very much. I didn't understand very well this sentence: "EDIT: Using realloc on memory allocated by new is indeed disallowed, as other answers have mentioned." You are asking other users to edit what they wrote? Because it is possible to use realloc with new and delete? – ncomputers Sep 17 '14 at 20:55
  • Edit signifies that I edited the post later to include that information; I'm reiterating that using realloc with new and delete is not allowed. – Sam van Kampen Sep 17 '14 at 20:57
  • That said, if you wanted to dive into the internals of g++, you might discover that mixing `new[]` and `realloc` like this "works" in the same way as what you've already talked about above works. That is to say, it's not guaranteed but maybe it does happen on one or more particular implementations just because of the "obvious" way to implement `new[]` for POD types. – Steve Jessop Sep 17 '14 at 20:59
2

C99 draft 7.20.3.4 says:

[#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.

You should not assume it.

And also: don't mix new and realloc as πάντα already wrote in the comments.

Marco A.
  • 43,032
  • 26
  • 132
  • 246
1

Nothing's guaranteed about realloc. It might shrink the block in place, or it might allocate a new one and copy the data. It might also fail.

An important point: realloc is only for reallocating memory that was previously allocated by malloc. In your code above, you are using new which has no equivalent for reallocation.

Also realloc actually returns the address of the new memory block, so in your code above you will be a) leaking this and b) referencing/freeing potentially already de-allocated memory.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • As he said, he wants to know if it'S safe (which it isn't). – Deduplicator Sep 17 '14 at 20:02
  • If there was a guarantee that `realloc` always returns the same pointer if the passed block is at least `size` bytes big, not saving the returned pointer because you know you are only shrinking the block anyway would be a valid choice. Thus the last paragraph is completely redundant with answering his question. – Deduplicator Sep 17 '14 at 20:07
  • @Deduplicator: The very first thing I said was "nothing's guaranteed about realloc". Perhaps you need to improve your reading comprehension? – Jonathan Potter Sep 17 '14 at 20:09
  • Ok, I'll try to rephrase that for you: He's providing code to illustrate his question, which is whether he can assume the pointer `realloc` returned is the pointer he passed, considering that he asks for shrinking (or at least not enlarging) the block. Your answer is: No, realloc does not provide that guarantee, and btw your example has a bug because realloc does not guarantee the block won't move. Do you now see how that last paragraph is at best completely redundant? – Deduplicator Sep 17 '14 at 20:14
  • @Deduplicator: No, because he's using the function incorrectly. How is pointing this out redundant? – Jonathan Potter Sep 17 '14 at 20:15