TArray remove* function call cause editor crash

Hi,

When I use TArray with iterator, and selectively remove some of the elements, editor will crash during execution.

source code:
.h

class TestElem {
public:
	int ID;
	TestElem(int idx);
	~TestElem();
};
    TArray<TestElem *> TestTArray2;

.cpp

for (i = 0; i < 10; ++i)
{
	TestTArray2.Add(new TestElem(i));
}

for (auto it : TestTArray2)
{
	UE_LOG(LogSanguo, Warning, TEXT("index for tarray2 %d"), TestTArray2.Find(it));

	if (it->ID == 3)
            { 
		// TestTArray2.RemoveAll([&it](TestElem *& Element) { return Element->ID == it->ID; });    --> cause crash
		// TestTArray2.RemoveAt(TestTArray2.Find(it), 1, true);    --> cause crash, in the code I comment them all
            }

	UE_LOG(LogSanguo, Warning, TEXT("remove done for tarray2"));
}

All remove* has issues, takes Remove() as an example, in the array.h, it looks like:

int32 Remove(const ElementType& Item)
{
	CheckAddress(&Item);

	// Element is non-const to preserve compatibility with existing code with a non-const operator==() member function
	return RemoveAll([&Item](ElementType& Element) { return Element == Item; });
}

the checkAddress function here is to “Checks that the specified address is not part of an element within the container.” Certainly it is in the range, and I want to remove it. So it will fail…

I believe removesingle() and remove*() has similar issue…

Could you help to confirm it? :slight_smile:

BRs

Hello Bevatron,

What is the callstack for the crash that you receive? I’ll need to see that information to know what is causing the issue as it could be multiple causes.

Hi Clark,

Thanks for the response.

You can check the call_stack.txt for details, briefly:

For RemoveSingle() call it looks like:
achineId:085FE37B45808B58B13A07996A6A627A
EpicAccountId:7f01c5707ae54a0ea3107f4922699b56

Unknown exception - code 00000001 (first/second chance not available)

"Assertion failed: (Index >= 0) &
(Index < ArrayNum) [File:D:\Program
Files\Epic
Games\4.8\Engine\Source\Runtime\Core\Public\Containers\Array.h]
[Line: 678] Array index out of
bounds: 9 from a

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\core\private\misc\outputdevice.cpp:355]
UE4Editor_Sanguo_7237!ABaseCharacter::DeleteArrayElem() [f:\documents\unreal projects\sanguo\source\sanguo\private\characters\basecharacter.cpp:484]

For remove():

MachineId:085FE37B45808B58B13A07996A6A627A
EpicAccountId:7f01c5707ae54a0ea3107f4922699b56

Unknown exception - code 00000001 (first/second chance not available)

"Assertion failed: Addr < GetData() ||
Addr >= (GetData() + ArrayMax)
[File:D:\Program Files\Epic
Games\4.8\Engine\Source\Runtime\Core\Public\Containers\Array.h]
[Line: 1411] Attempting to add

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\core\private\misc\outputdevice.cpp:355]
UE4Editor_Sanguo_1130!TArray::Remove() [d:\program files\epic games\4.8\engine\source\runtime\core\public\containers\array.h:1965]

I should’ve expected the asserts to get cut off like they did. Could you post the crash logs themselves as it won’t limit itself as the callstack does? They can be found in the project directory under Saved > Logs.

In the meantime, it seems like the issue in RemoveSingle() is that you may be, somewhere in the code, referencing an element of the array that is out of the bounds of the array and doesn’t exist. I’m unsure about the other one so far.

It’s not safe to remove from an array while iterating through it. Remove/Add during iteration can cause the memory to be reallocated, causing a crash. The better way to structure the code is to store off the ID of the item you are looking for and then use that to remove after the loop exits. For instance:

int32 ItemIndex = -1;
for (auto item : Items)
{
	if (item.ID == 3)
	{
		ItemIndex = TestTArray2.Find(item);
		break;
	}
}
if (Items.IsValidIndex(ItemIndex))
{
	Items.RemoveAt(ItemIndex);
}

However, that code is the slower way to do it, since Find also traverses the array doing comparisons. Better code is:

int32 ItemIndex = -1;
for (int32 Index = 0; Index < Items.Num(); Index++)
{
	if (Items[Index].ID == 3)
	{
		ItemIndex = Index;
		break;
	}
}
if (Items.IsValidIndex(ItemIndex))
{
	Items.RemoveAt(ItemIndex);
}

That’s it. It’s working on my side.

Jeo & Clark, thanks alot~ :slight_smile: