#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
. Именно поэтому это сработало после того, как я «сбросил» указатель на исходный адрес. Приятно это знать!