изменение указателя динамически размещаемого массива

Когда мы динамически объявляем что-то вроде этого

int *ptr = new int [100];

а затем измените адрес указателя (т.е. укажите его на что-то другое)

int pointer[5] = {1,2,1,3,1,};
ptr = pointer ; 

что теперь происходит с той памятью, которая содержала 100 целых чисел?

Есть ли способ восстановить контроль над этой памятью? а если нет, то приводит ли это к утечке памяти?


person RazaUsman_k    schedule 19.03.2017    source источник


Ответы (3)


что теперь происходит с той памятью, которая содержала 100 целых чисел?

Он «утек», потому что вы потеряли единственный доступ к нему.

Есть ли способ восстановить контроль над этой памятью?

Нет, если вы не позаботитесь о создании другого дескриптора перед повторным назначением ptr.

int* ptr = new int[100];
int* ptr2 = ptr;
...
ptr = pointer;
delete [] ptr2;

В реальном коде вы не будете полагаться на необработанные указатели на выделенную память, а будете использовать тот тип RAII, который лучше всего подходит для вашей задачи (например, std::vector, std::unique_ptr и т. д.).

person juanchopanza    schedule 19.03.2017
comment
и ОС обнаружит эту утечку памяти и освободит ее после завершения программы. Верно? - person RazaUsman_k; 20.03.2017
comment
@RazaUsman_k Для часто используемых ОС да. Но это все равно следует считать ошибкой. Также обратите внимание, что для пользовательских типов отсутствие запуска деструкторов может привести к странностям. - person juanchopanza; 20.03.2017
comment
@juanchopanza Есть ли какие-нибудь хорошие ссылки/чтения по этой теме для дальнейшего разъяснения? - person 0_0perplexed; 31.10.2020

В приведенном вами образце ptr является единственным указателем, который ссылается на сотню int, созданную new int [100] (т.е. указывает на первый элемент).

Изменение значения ptr не влияет на сотню int. Это означает, что ptr больше не содержит адрес первого элемента.

На практике после присваивания ptr = pointer говорят, что выделенная память утекла — она была выделена, в стандартном C++ нет способа получить к ней доступ, а в стандартном C++ нет возможности освободить ее (например, сделать потребляемую память доступной для перераспределение в другом выражении new).

Единственный способ сохранить контроль над памятью в вашем случае - сохранить значение ptr в другой переменной (или члене структуры, или элементе массива указателей,....) ПЕРЕД его переназначением. Если значение ptr сохраняется до переназначения ptr, а место, где оно хранится, все еще существует, значение можно получить.

На практике было бы лучше использовать стандартный контейнер (например, std::vector<int>) и не использовать новое выражение в вашем коде. Таким образом, содержимое контейнера доступно до тех пор, пока контейнер существует (и перестает существовать, насколько это касается вашей программы, когда контейнер существует).

person Peter    schedule 19.03.2017

Любой блок памяти, выделенный с помощью new, недоступен до тех пор, пока он не будет освобожден либо соответствующим delete, либо когда процесс, в котором была выделена память, завершится.

Распределитель памяти на самом деле не знает, какие ссылки вы поддерживаете на выделенных объектах. Он просто позволяет вам освободить соответствующие блоки памяти, если вам удастся предоставить действительные адреса.
Как вы будете отслеживать эти адреса, полностью зависит от вас.

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

Это становится утечкой памяти, если «провал памяти» происходит непреднамеренно, тем более, если это происходит неоднократно.

Например, если вам нужны данные с таким же сроком жизни, как и у процесса, вы можете выполнить однократное выделение памяти и никогда не освобождать память (поскольку она будет освобождена автоматически по завершении). Это может привести в ужас фанатиков хороших практик, но, тем не менее, не будет считаться утечкой памяти.

С другой стороны, есть и другие способы запутать ваш распределитель памяти: передача недопустимого указателя на delete (включая ссылку на уже освобожденный объект, т.е. удаление одного и того же экземпляра объекта более одного раза, или указатель на что-то, что не был выделен new, как локальная или глобальная переменная) откроет вам чудесную область неопределенного поведения, которое может привести к случайным потерям памяти, повреждению данных или ядерным расплавлениям.

person kuroi neko    schedule 19.03.2017