Уточните висячий указатель в 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() прекращается?

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
}

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

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

  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 загромождает ваш код. Здесь нужна некоторая мудрость, чтобы оценить достоинства и недостатки.

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

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

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

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

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

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

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

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

Другие вопросы по тегам