Уточнить висячий указатель в C/C++

Меня немного смущает оборванный указатель в C/C++

void remove(){
Node* curr = new Node(10);
Node* pt = curr;
delete curr;

// do something here
// do other thing here
}

Я предполагаю, что Node* pt все еще висит указатель до того, как функция remove() будет завершена?

Мне не нужно беспокоиться об указателе Node* pt после завершения remove()?


person 1234    schedule 29.05.2016    source источник
comment
pt — локальная переменная этой функции; после того, как управление переходит из функции, вам не нужно об этом беспокоиться.   -  person Beta    schedule 29.05.2016
comment
Не существует языка под названием C/C++. Я удаляю тег C, так как это явно вопрос C++.   -  person fuz    schedule 30.05.2016


Ответы (4)


Когда вы вызываете delete curr, значение, хранящееся в curr, не изменяется, но память в этом месте возвращается системе.

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

Node* curr = new Node(10);

ради аргумента давайте представим, что выделенная память имеет адрес 0x1000, это означает, что curr теперь имеет значение 0x1000.

Node* pt = curr;

pt теперь также имеет значение (указывает на местоположение) 0x1000.

delete curr;

Это возвращает в систему память по адресу 0x1000. ОДНАКО, на данном этапе и curr, и pt по-прежнему содержат значение 0x1000.

Они оба болтаются указатели.

Висячие указатели неизбежны при использовании необработанных указателей, и они не являются неявно плохими. Вам просто нужно быть осторожным, чтобы не использовать висячий указатель или возвращать его.

void badfunc() {
    char* p = new char[10];
    delete p;
    return p;  // use of dangling pointer
}
person kfsone    schedule 29.05.2016

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

В закомментированной части remove, но curr и pt являются висячими указателями, поскольку они оба указывают на объект, который был удален. Пока этот код не разыменовывает их, проблем нет.

person David Schwartz    schedule 29.05.2016
comment
Ваш вопрос, похоже, основан на ложном предположении, что с оборванным указателем что-то не так. Любить это. +1 - person skypjack; 30.05.2016
comment
Вот почему я прихожу к SO, чтобы попросить разъяснений, для чего SO? - person 1234; 30.05.2016

  1. Относительно вашего вопроса: "Я предполагаю, что Node* pt все еще запутывает указатель до завершения функции remove()?"
    Да, после вызова delete curr; блока памяти ранее указанный как Node* curr, так и Node* pt, освобождается. Вы не должны использовать ни значения curr, ни pt после вызова delete curr; (они оба имеют одинаковое значение, и оно недействительно). Не предполагайте, что вызов delete изменяет значение curr. Это не так - вы можете проверить это, напечатав оба значения до и после вызова delete curr; следующим образом:

    printf("%d, %d", curr, pt);

  2. Относительно вашего вопроса: "Мне не нужно беспокоиться об указателе Node* pt после завершения функции remove()?"
    Действительно, после того как remove() завершает работу как Node* curr и Node* pt перестают существовать. Они также находятся вне области действия (недоступны) за пределами remove(). Поэтому вам не нужно беспокоиться о них.

  3. Кроме того, объекты/данные, хранящиеся в памяти, на которые ранее указывали Node* curr и Node* pt, освобождаются/уничтожаются delete curr;, поэтому вам также не нужно об этом беспокоиться.

  4. Иногда рекомендуется избегать висячих указателей, устанавливая для них значение NULL. Это не снимает проблему, но, по крайней мере, ясно проявляется, если вы используете такой указатель случайно. Это связано с тем, что попытка разыменования указателя NULL приводит к ошибке сегментации памяти, поэтому вы, по крайней мере, получите воспроизводимую ошибку времени выполнения для поиска своей ошибки. Другие говорят, что слепая установка каждого неиспользуемого указателя в NULL загромождает ваш код. Здесь требуется некоторая мудрость, чтобы взвесить преимущества и недостатки.

person Artur Opalinski    schedule 29.05.2016

Висячий указатель относится к указателю, который указывает на более недействительный объект. Это не должно быть основано на new/delete или malloc/free: на самом деле, не указатели могут болтаться. Можно сказать, что любая ссылка на другой объект или ресурс, где ссылка больше недействительна, но ссылка «не знает этого», «болтается».

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

Так что не разыменовывайте висячие указатели.

После delete оба указателя болтаются.

Как правило, полезно рассуждать о программе, если ваш код требует, чтобы переменные требования находились в известных состояниях, которые вы можете определить по их типу и/или имени. Одним из примеров может быть «не иметь висячих указателей, установить для них значение null сразу после удаления»: тогда, если вы всегда инициализируете указатели значением null при создании, каждый указатель либо действителен, либо указывает на значение null.

Делать это с постоянными данными — отличная идея; это с локальными переменными в крошечных функциях часто добавляет больше шума, чем помогает.

Другой подход заключается в том, чтобы предпочесть использовать интеллектуальные указатели, но у них есть свои ловушки. Интеллектуальные указатели с подсчетом ссылок и маркировкой и очисткой превращают висячие указатели в утечку ресурсов! И уникальный ptr не имеет безопасного типа указателя «наблюдатель».

Локальный указатель после окончания его области действия перестает существовать. Это не может быть висячий указатель, так как это не указатель.

person Yakk - Adam Nevraumont    schedule 29.05.2016