бесплатно: недопустимый указатель при удалении динамически созданного массива

#c #arrays #pointers #dynamic-memory-allocation #delete-operator

Вопрос:

В настоящее время я практикуюсь в арифметике указателей и не могу понять, почему этот код заканчивается ошибкой «free(): недопустимый указатель», когда дело доходит до выполнения последней строки delete [] arr; .

Я понял, что тот же код запускается без каких-либо проблем при использовании программы C4Droid на моем телефоне Android (с использованием компилятора «G Bionic»), но на моем планшете, использующем GCC, он просто не работает. Единственный способ обойти это-убедиться, что массив arr в настоящее время указывает на первый адрес. В тот момент, когда этого не происходит, кажется, что он терпит неудачу. Я не знаю, должно ли это произойти и на моем телефоне, или это как-то связано с проблемой конфигурации в среде IDE, которую я использую на своем планшете (пробовал с кодовыми блоками и codelite). Не могли бы вы, пожалуйста, помочь?

Я также хорошо понимаю, что инициализирую значения для двух первых мест массива, в то время как технически возможно выбрать размер массива меньше этого. Это просто тесты.

 #include <string>
#include <iostream>

int main()
{
    int *arr{};
    size_t size{};
    std::cout << "Please enter the size of the array you want to create: "; std::cin >> size;
    arr = new int[size];
    *(arr   0) = 5;
    *(arr   1) = 10;
    std::cout << "  arr: " <<   arr << " (pointer incrementation, we move by an int and arr is now arr   1)" << std::endl;  
    delete [] arr;
    return 0;
}
 

Комментарии:

1. «Я не знаю, должно ли это сработать и на моем телефоне» — Это неопределенное поведение. Он может делать все, что захочет, включая сбой, не сбой, внешне работает правильно, форматирует ваш жесткий диск или заставляет демонов вылетать из вашего носа . Вы можете использовать только delete указатель, выделенный с new помощью, и ничего больше.

2. arr это не то , что было выделено при увеличении указателя. arr new

3. «запуски без каких-либо проблем» должны быть «запуски без каких-либо проблем, которые я мог бы обнаружить «. Была проблема, но симптомы под Android были очень тонкими.

Ответ №1:

Вы не удаляете тот же указатель, который вы выделили — arr переместили указатель. Вызов delete[] любого указателя, который не был выделен, new type[] является неопределенным поведением — он может работать, он может катастрофически потерпеть неудачу и может сделать что угодно между ними, но вам действительно не следует полагаться на него.

Комментарии:

1. Спасибо, все чисто, кау! Я ошибочно подумал, что delete [] arr это все равно сработает, потому arr что указывал на следующий адрес в массиве, который я динамически выделил. Я не понимал, что простое изменение адреса, даже в этом случае, испортит освобождение.

Ответ №2:

После этого утверждения, в котором вы увеличили указатель arr

 std::cout << "  arr: " <<   arr << " (pointer incrementation, we move by an int and arr is now arr   1)" << std::endl;  
 

указатель теперь не имеет адреса выделенной памяти.

Итак, следующее утверждение

 delete [] arr;
 

вызывает неопределенное поведение.

Вы должны передать оператору delete [] указатель с тем же значением (адресом), которое было возвращено оператором new.

Вы могли бы написать, например

 delete [] --arr;
 

использование оператора декремента для сброса указателя к его исходному значению.

Комментарии:

1. Большое спасибо. Теперь это действительно имеет большой смысл. Это означает, что я всегда должен быть уверен, что сохраню исходный адрес где-нибудь на случай, если я когда-нибудь выполню подобную арифметику указателя, чтобы я мог восстановить значение указателя до того, каким оно было раньше, прежде чем я вызову delete [] arr . Именно поэтому это сработало после того, как я «сбросил» указатель на исходный адрес. Приятно это знать!