Kontekst: Próbuję zawinąć głowę wokół wskazówek, widzieliśmy je kilka tygodni temu w szkole i podczas dzisiejszych ćwiczeń wpadłem na głupiego? problem, może to być dla ciebie bardzo proste, ale mam niewielkie doświadczenie w programowaniu.
Widziałem w SO sporo pytań dotyczących usuwania wskaźników, ale wszystkie wydają się być związane z usunięciem klasy, a nie „prostym” wskaźnikiem (lub jakimkolwiek właściwym terminem), oto kod, który próbuję biegać:
#include <iostream>;
using namespace std;
int main() {
int myVar,
*myPointer;
myVar = 8;
myPointer = &myVar;
cout << "delete-ing pointers " << endl;
cout << "Memory address: " << myPointer << endl;
// Seems I can't *just* delete it, as it triggers an error
delete myPointer;
cout << "myPointer: " << myPointer << endl;
// Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4:
// pointer being freed was not allocated
// *** set a breakpoint in malloc_error_break to debug
// Abort trap: 6
// Using the new keyword befor deleting it works, but
// does it really frees up the space?
myPointer = new int;
delete myPointer;
cout << "myPointer: " << myPointer << endl;
// myPointer continues to store a memory address.
// Using NULL before deleting it, seems to work.
myPointer = NULL;
delete myPointer;
cout << "myPointer: " << myPointer << endl;
// myPointer returns 0.
}
Więc moje pytania to:
- Dlaczego pierwszy przypadek nie zadziała? Wydaje się, że użycie i usuwanie wskaźnika jest najprostsze? Błąd mówi, że pamięć nie została przydzielona, ale „cout” zwrócił adres.
- W drugim przykładzie błąd nie jest wyzwalany, ale wykonanie cout wartości myPointer nadal zwraca adres pamięci?
- Czy numer 3 naprawdę działa? Wydaje mi się, że działa, wskaźnik nie przechowuje już adresu, czy to właściwy sposób na usunięcie wskaźnika?
Przepraszam za długie pytanie, chciałem wyjaśnić to tak jasno, jak to tylko możliwe, a także powtórzyć, mam niewielkie doświadczenie w programowaniu, więc jeśli ktoś mógłby odpowiedzieć na to używając terminów laika, byłoby to bardzo wdzięczne!
c++
pointers
delete-operator
leopic
źródło
źródło
delete
to, co tynew
. Po usunięciu wskaźnika nie jest również wymagane, aby ustawiał się na NULL. Jeśli chcesz mieć tam bezpieczeństwo, użyj inteligentnych wskaźników, które zwalniają pamięć i wyświetlają błędy, gdy próbujesz uzyskać do nich dostęp, gdy czegoś nie zawierają.reset
a to uwalnia stare. Aby go zwolnić bez wymiany, dzwoniszrelease
. Kiedy wychodzi poza zakres, jest niszczona i może zwolnić pamięć w zależności od jej typu.std::unique_ptr
jest przeznaczony tylko dla jednego właściciela.std::shared_ptr
zwalnia go, gdy ostatni właściciel przestaje być właścicielem zasobu. Są również wyjątkowo bezpieczne. Jeśli przydzielisz zasób za pomocą jednego, a następnie napotkasz wyjątek, zasób zostanie prawidłowo zwolniony.Odpowiedzi:
1 i 2
myVar = 8; //not dynamically allocated. Can't call delete on it. myPointer = new int; //dynamically allocated, can call delete on it.
Pierwsza zmienna została przydzielona na stosie. Możesz wywołać usuwanie tylko w pamięci przydzielonej dynamicznie (na stercie) za pomocą
new
operatora.3.
myPointer = NULL; delete myPointer;
Powyższe nic nie dało . Nic nie uwolniłeś, ponieważ wskaźnik wskazywał na NULL.
Nie należy robić następujących rzeczy:
myPointer = new int; myPointer = NULL; //leaked memory, no pointer to above int delete myPointer; //no point at all
Wskazałeś go na NULL, pozostawiając wyciek pamięci (nowy przydzielony int). Powinieneś uwolnić wspomnienie, na które wskazywałeś. Nie ma już możliwości uzyskania dostępu do tego przydzielonego
new int
, stąd wyciek pamięci.Właściwy sposób:
myPointer = new int; delete myPointer; //freed memory myPointer = NULL; //pointed dangling ptr to NULL
Lepszy sposób:
Jeśli używasz C ++, nie używaj surowych wskaźników. Zamiast tego użyj inteligentnych wskaźników, które mogą obsłużyć te rzeczy przy niewielkim nakładzie pracy. C ++ 11 zawiera kilka .
źródło
delete myPointer
zwalnia*myPointer
. To jest poprawne. AlemyPointer
nadal wskazuje miejsce w pamięci, które zostało zwolnione i nie powinno być używane, ponieważ jest to UB. Będzie niedostępna po zakończeniu zakresu tylko JEŚLI była to najpierw zmienna lokalna.new
każdego dnia. (Jestem tandetny!)NULL
zapobiega późniejszemu nadużywaniu go.Uważam, że nie w pełni rozumiesz, jak działają wskaźniki.
Kiedy masz wskaźnik wskazujący na jakąś pamięć, są trzy różne rzeczy, które musisz zrozumieć:
- istnieje „to, na co wskazuje” wskaźnik (pamięć)
- ten adres pamięci
- nie wszystkie wskaźniki muszą mieć skasowaną pamięć: ty tylko potrzeba usunięcia pamięci, która została przydzielona dynamicznie (
new
operator używany ).Wyobrażać sobie:
int *ptr = new int; // ptr has the address of the memory. // at this point, the actual memory doesn't have anything. *ptr = 8; // you're assigning the integer 8 into that memory. delete ptr; // you are only deleting the memory. // at this point the pointer still has the same memory address (as you could // notice from your 2nd test) but what inside that memory is gone!
Kiedy to zrobiłeś
ptr = NULL; // you didn't delete the memory // you're only saying that this pointer is now pointing to "nowhere". // the memory that was pointed by this pointer is now lost.
C ++ pozwala na próbę
delete
wskazania wskaźnika,null
ale tak naprawdę nic nie robi, po prostu nie daje żadnego błędu.źródło
Wskaźniki są podobne do zwykłych zmiennych, ponieważ nie trzeba ich usuwać. Są one usuwane z pamięci na końcu wykonywania funkcji i / lub na końcu programu.
Możesz jednak użyć wskaźników do przydzielenia `` bloku '' pamięci, na przykład w ten sposób:
int *some_integers = new int[20000]
Spowoduje to przydzielenie miejsca w pamięci na 20000 liczb całkowitych. Przydatne, ponieważ stos ma ograniczony rozmiar i możesz chcieć zadzierać z dużym ładowaniem „int” bez błędu przepełnienia stosu.
Za każdym razem, gdy wywołujesz new, powinieneś następnie „usunąć” na końcu programu, ponieważ w przeciwnym razie wystąpi wyciek pamięci, a część przydzielonej pamięci nigdy nie zostanie zwrócona do użycia przez inne programy. Aby to zrobić:
delete [] some_integers;
Mam nadzieję, że to pomoże.
źródło
nowy nigdy nie jest nazywany. Zatem adres, który drukuje cout jest adresem lokalizacji pamięci myVar lub wartością przypisaną do myPointer w tym przypadku. Przez pisanie:
mówisz:
Zwraca adres wskazujący lokalizację pamięci, która została usunięta. Ponieważ najpierw tworzysz wskaźnik i przypisujesz jego wartość do myPointer, po drugie go usuwasz, a po trzecie drukujesz. Jeśli więc nie przypiszesz innej wartości do myPointer, usunięty adres pozostanie.
NULL równa się 0, usuwasz 0, więc nic nie usuwasz. I logiczne jest, że wypisuje 0, ponieważ zrobiłeś:
myPointer = NULL;
co jest równe:
myPointer = 0;
źródło
źródło
int value, *ptr; value = 8; ptr = &value; // ptr points to value, which lives on a stack frame. // you are not responsible for managing its lifetime. ptr = new int; delete ptr; // yes this is the normal way to manage the lifetime of // dynamically allocated memory, you new'ed it, you delete it. ptr = nullptr; delete ptr; // this is illogical, essentially you are saying delete nothing.
źródło