C : проблема с памятью при удалении указателя, созданного new

#c #pointers #memory-management #delete-operator

#c #указатели #управление памятью #оператор удаления

Вопрос:

Меня немного смущает следующий код

 int main()
{
    int* a = new int{12};
    int* b = new int;

    b = a;
    delete a;

    delete b;

    return 0;
}
 

Код возвращает ошибку, которая

 a.out(27538,0x10ade7e00) malloc: *** error for object 0x7f8c18504160: pointer being freed was not allocated
a.out(27538,0x10ade7e00) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out
 

Мой вопрос в том, что, когда я удаляю a, автоматически ли он удаляет b? Каков здесь механизм, я немного теряюсь.

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

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

2. Оба a и b указывают на одну и ту же память. Удаление обоих приводит к удалению одного и того же блока памяти 2 раза, что является неопределенным поведением. Вы также пропустили память, на которую b первоначально указывали. Это невозможно удалить, потому что адрес был удален при b = a; назначении.

3. Вы совершаете ошибку, глядя на имя переменной вместо того, чтобы смотреть на значение переменной. Если вы отладили код, вы увидите, что после этих вызовов new оба a и b имеют разные значения. Важны именно эти значения, а не имя. Когда вы выполнили delete вызов, вы дважды удаляете одно и то же значение, поскольку вы присвоили значение a to b .

Ответ №1:

 b = a;    // here the int that b pointed at "leaks" (you have no way of deleting it)

delete a; // here "a" is deleted first

delete b; // here "a" is deleted a second time (undefined behavior)
 

Когда вы присваиваете значение a to b , значение (адрес, если хотите) b , ранее содержащееся, забывается. Оба a и b затем указывают на один и тот же объект. Затем вы потеряли все возможности для delete исходного объекта b , на который указывали. Когда вы тогда delete b , вы на самом деле пытаетесь удалить a второй раз.

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

1. Я бы сказал, что b = a; это строка, в которой «b» протекает.

2. @eerorika Я могу это купить. Я выбираю «потерянный», но, вероятно, правильнее сказать «утечка» прямо там.

Ответ №2:

Давайте пройдемся по коду построчно.

 int* a = new int{12};
 

Это создает новую переменную, вызываемую a типом int* , и инициализирует ее, указывая на вновь выделенное целое число, которому присвоено значение 12.

 int* b = new int;
 

Это создает новую переменную, вызываемую b типом int* , и инициализирует ее, указывая на вновь выделенное целое число.

 b = a;
 

Это изменяет b значение, на которое указывает a . Значение, возвращенное во втором вызове new , теперь потеряно, и эта память утекает, поскольку передать ее больше невозможно delete .

 delete a;
 

При этом удаляется объект a , на который указывает объект, выделенный первым.

 delete b;
 

Упс, это попытка удалить объект b , на который указывает, но b не указывает ни на один существующий в данный момент объект. Тот, который был выделен первым, был просто удален, и никакого указателя на второй не существует. Так что это ошибка.

Я подозреваю , что вы думаете , что delete a; это удаляет a . Это не так. Он удаляет любой объект a , на который указывает , и требует , чтобы a он был указателем и указывал на действительный объект , который был выделен new .

Ответ №3:

При удалении указателя Все указатели, ссылки, итераторы и т. Д., указывающие на уничтоженный объект, Становятся недействительными. Из-за назначения b = a b указывает на тот же объект, что и a, поэтому, когда a удаляется, b становится недействительным. Удаление недопустимого указателя имеет неопределенное поведение.

Обратите внимание, что это b = a делает невозможным удаление выделения, на которое ранее указывал b. Это называется утечкой памяти.