Что такое висячий указатель?
Я знаю, что это довольно распространенный вопрос, но все же новый для меня!
Я не понимаю концепцию висящего указателя, гуглюсь и пишу тестовые методы, чтобы найти его.
Мне просто интересно, это свисающий указатель? Так как какой бы пример я не нашел, он возвращал что-то, здесь я пробую что-то подобное
Спасибо!
void foo(const std::string name)
{
// will it be Dangling pointer?!, with comments/Answer
// it could be if in new_foo, I store name into Global.
// Why?! And what is safe then?
new_foo(name.c_str());
}
void new_foo(const char* name)
{
// print name or do something with name...
}
8 ответов
Висячий указатель - это указатель, который указывает на недопустимые данные или на данные, которые больше не являются допустимыми, например:
Class *object = new Class();
Class *object2 = object;
delete object;
object = nullptr;
// now object2 points to something which is not valid anymore
Это может происходить даже в объектах, выделенных стеком:
Object *method() {
Object object;
return &object;
}
Object *object2 = method();
// object2 points to an object which has been removed from stack after exiting the function
Указатель возвращается c_str
может стать недействительным, если строка впоследствии изменена или уничтожена. В вашем примере вы, кажется, не изменяете его, но поскольку неясно, что вы собираетесь делать const char *name
невозможно знать, безопасен ли ваш код или нет.
Например, если вы храните указатель где-то, а затем соответствующая строка уничтожается, указатель становится недействительным. Если вы используете const char *name
просто в объеме new_foo
(например, в целях печати), тогда указатель останется действительным.
Висячий указатель - это (не NULL) указатель, который указывает на нераспределенную (уже освобожденную) область памяти.
Приведенный выше пример должен быть верным, учитывая, что строка не изменяется через new_foo.
Взято отсюда. Хотя, даже если это для C, то же самое для C++.
Висячий указатель
Если какой-либо указатель указывает адрес памяти какой-либо переменной, но после того, как какая-то переменная была удалена из этой ячейки памяти, тогда как указатель все еще указывает на эту ячейку памяти. Такой указатель известен как висячий указатель, а эта проблема известна как проблема висячего указателя.
Первоначально
Потом
пример
#include<stdio.h>
int *call();
int main() {
int *ptr;
ptr = call();
fflush(stdin);
printf("%d", *ptr);
return 0;
}
int * call() {
int x=25;
++x;
return &x;
}
Его вывод будет мусором, потому что переменная x является локальной переменной. Его область действия и время жизни находятся в вызове функции, следовательно, после того, как возвращаемый адрес переменной x стал мертвым, а указатель все еще указывает, ptr все еще указывает на это местоположение.
В качестве стиля я объясняю висячий указатель как "указатель, который все еще существует, даже если объект, на который он указывал, больше не существует".
В вашем случае указатель name
существует в течение более короткого периода, чем объект, на который он указывает. Так что это никогда не болтается.
Внутри обычных классов C++ указатели в течение очень короткого периода времени болтаются, внутри деструкторов. Это потому что delete
заявление до последнего }
деструктора, в то время как сам указатель перестает существовать в последний }
, Если вы не хотите беспокоиться об этом, используйте, например, unique_ptr<T>
, T*
указатель будет очень коротко болтаться внутри unique_ptr::~unique_ptr
деструктор, который совершенно безопасен.
Висячие указатели - это ситуация, когда у вас есть действительные указатели в стеке, но они указывают на недопустимую память. Вы можете попасть в эту ситуацию, когда освободите память кучи до того, как освободятся указатели в стеке.
Это проблема безопасности. Потому что, когда вы освобождаете память, мы сообщаем операционной системе, что нам больше не нужен этот раздел памяти. Таким образом, ОС будет отмечать этот фрагмент памяти как готовый для выделения и выделения другим приложениям, когда они запрашивают память.
Обычно в C++ память выделяется и освобождается по общему шаблону. Конструктор в классе вызывается при инициализации класса, и это подходящее место для выделения памяти в куче. Деструктор будет вызываться, когда экземпляр класса выходит за пределы области видимости, и это подходящее место для освобождения памяти из кучи. Предположим, мы уже создали класс, который выполняет выделение и освобождение памяти в конструкторе и деструкторе соответственно.
int main() {
SomeClass pointer1 = SomeClass();
SomeClass pointer2 = pointer1;
}
В приведенном выше примере кода объявлены две переменные, но обе имеют одинаковое значение. Когда конструктор вызывается, он выделяет кучу памяти. Затем мы объявляем еще одну переменную и присваиваем то же значение. В C++ обычно, когда вы присваиваете значение сложного типа, оно выполняет неглубокую копию (если вы явно не реализовали конструктор копирования) вместо глубокой копии. Это означает, что в стек копируется единственный указатель, но не память кучи. На самом деле не рекомендуется копировать динамическую память из соображений производительности. Теперь окончательный макет памяти выглядит так, как будто у нас есть два указателя, указывающие на одну и ту же динамическую память.
Теперь, когда функция завершена с выполнением, локальные переменные выходят за пределы области видимости и вызывает деструктор. Сначала указатель2 вызывает деструктор, который освобождает память кучи. В этот момент указатель1 становится висячим указателем. Он указывает на уже освобожденную память.
Из этого примера мы поняли, что основной причиной зависшего указателя является наличие нескольких владельцев для одного и того же ресурса. Потому что, когда один указатель освобождает память, другие указатели становятся висячими указателями.
//Declaring two pointer variables to int
int * ptr1;
int * ptr2;
// Allocating dynamic memory in the heap
ptr1 = new int;
ptr2 = ptr1; // Having both pointers to point same dynamic memory location
//deleting the dynamic memory location
delete ptr1;
ptr1 = nullptr;
//ptr2 is still pointing the already deleted memory location
//We call ptr2 is a dangling pointer
Указатель, содержащий адрес, которого нет или он был удален.
Проще говоря, мы можем сказать, что указатель содержит адрес места, память которого была удалена.
Вы можете понять это с помощью простой диаграммы:
Проблема с висящим указателем и висящим указателем Если какой-либо указатель указывает адрес памяти какой-либо переменной, но после удаления некоторой переменной из этой ячейки памяти, в то время как указатель все еще указывает на эту ячейку памяти.
Этот указатель называется висячим указателем, а проблема, которая возникает в то время, называется висячей указателем.
Вот несколько примеров: Dangling Pointer и проблема с висящим указателем