Download

Question about TArray shrink

When a TArray shrinks its size by either removing items or calling shrink(), is there a chance the array can reallocate causing all existing elements to be copied somewhere else and as a result existing references are invalidated ?

Let’s say I have this simple example

TArray<int32> Arr = { 0, 2, 3, 4, 5 };
int32* PtrToFirstElem = &Arr[0];

Arr.RemoveAt(1); //Allowed to shrink.

I hold a pointer to the first element in the array, after that I remove index 1 and assuming the array will shrink in this case.
Does shrinking the array causes PtrToFirstElem to be pointing to invalid data as a result of reallocation ?

I have tested by comparing the new reference and it shows that the first element maintained its reference but I would like to know if completely reallocating the array is a possibility.

if(&Arr[0] == PtrToFirstElem) //True

Thank you.

Yes. It is not safe to store pointers to array members if you are adding or removing entries, causing it to resize.

Thank you for replying.

Adding items will definitely cause issues in that situation however I’m more interested in the shrink aspect of TArray.

Take this for example, I also tried this on a timer and same result.
This always says No (Edit: YES), the first element of the array maintained its reference throughout the multiple shrinking operations.
I’m still not going to trust that this behavior is consistent especially in other environments but it’s interesting nonetheless.

const int32 Size = 5000;
TArray<int32> SomeArr;
SomeArr.Reserve(Size);
for(int32 i = 0; i < Size; i++)
{
	SomeArr.Emplace(i);
}

int32* PtrToFirstElem = &SomeArr[0]; //Store pointer of first element.

while (SomeArr.Num() > 1) 
{
	SomeArr.RemoveAt(1);
	SomeArr.Shrink(); //Force to shrink.
}

if (PtrToFirstElem == &SomeArr[0])
{
	LOG(TEXT("YES"));
}
else
{
	LOG(TEXT("No"));
}

The default TArray allocator uses realloc, so yeah, you should get the same address as before.
But I am not sure if this is defined by the standard or it is an implementation detail.

BTW RemoveAt has overloads with an additional parameter that you can control whether the reallocation should take place or not.
void RemoveAt(SizeType Index, CountType Count, bool bAllowShrinking = true)

Your code prints “No” when it doesn’t maintain its position.

You likely aren’t going to see it change every time. If you call shrink, arrays still maintain slack at the end to fit in the next larger allocator bin. For example, you might want to shrink from 8 to 7 elements, but the allocator only has bins available that fit 6, 8, 10, etc elements. Then you get an array with 7 elements and 1 slack and no reallocation is done. However, if you remove another element and shrink again, you’d get a reallocation. Those numbers are just examples, I don’t know actual bin sizes used by the allocator off hand.

For a start, it uses FMemory::Realloc, which does whatever Unreal wants it to do.

And realloc is by no means required to give you the same address. It sometimes does, if you only reduced the size by a little, and the allocator doesn’t have any more sensible place to put this allocation (i.e. not small enough to go down a bin size).

@Emaer Thank you, I’m aware of bAllowShrinking which I ended up using to prevent reallocation if I’m holding a reference and don’t want it to change.

@Zeblote Exactly, it printed “YES” 100% of the time and I printed the allocated size just to make sure and the shrinking happened several times. Essentially the array shrunk in place which is probably not a guaranteed behavior.

Thanks for the replies I appreciate it.


For additional complexity, the editor also uses a different allocator (TBB) than shipping (Binned2). It’s possible that TBB is just not bothering to move the allocation to a smaller bin, but Binned2 definitely does.

Oh my bad I meant to say it always says Yes.

That’s very interesting, to be safe I’m going to assume that a reallocation is definitely possible and should expect it, especially when dealing with different operating systems.

Cheers for the info I appreciate it.