Musiałbyś poczytać jak działa operator delete[]. Konkretnie skąd wie jakiej wielkości jest tablica jaką ma usunąć. Z tego, co wiem, to jest zależne od kompilatora, ale przynajmniej niektóre kompilatory robią coś takiego:
Rezerwujesz tablice (załóżmy) 5 intów: int* ptr = new int[5]; A kompilator poza samą tablicą zapisuje sobie jej rozmiar tuż przed pierwszym elementem. Także w pamięci to wygląda tak:
[rozmiar][0][1][2][3][4]. Wskaźnik ptr po alokacji wskazuje na [0] (na pierwszy element). Kluczową informacją do zrozumienia tego, jest, że nazwa tablicy jest wskaźnikiem do jej pierwszego (0) elementu, ale już to pewnie zauważyłeś przy dynamicznej alokacji.
Zatem przy zwalnianiu pamięci operator delete[] może sobie pobrać wielkość tablicy z *(ptr - 1) i na tej podstawie zwalniać w tym przypadku 5 * sizeof(int) bajtów pamięci.
Ale jeżeli przestawisz wskaźnik ptr i (załóżmy) że teraz wskazuje na [3] to delete[] szuka rozmiaru w [2]. A jeśli (załóżmy) znajduje sie tam liczba 17 (losowa liczba jaka mi przyszła do głowy), to nie dosyć, że delete[] nie próbuje zwolnić całej zaalokowanej pamięci, to jeszcze najprawdopobniej spróbuje zwolnić pamięć ze stosu programu (nie zaalokowanej dynamicznie), albo w ogóle do niego nie należącej, więc crash jest pewny.
Być może wygląda to inaczej, ale na pewno można sporo namieszać.
Btw. od kiedy od c++11 wprowadzono std::shared_ptr i std::unique_ptr, powinno się mieć dobry powód żeby alokować pamięć "na czysto".