2016-06-17 17 views
6

Dziękuję za sprawdzenie mojego pytania. Właśnie napotkałem naprawdę zasadniczy problem podczas wdrażania BST, czyli "jaka jest różnica w różnych podejściach do przypisywania wskaźnika?" Wszyscy wiemy przypisać punkt może używać:Jaka jest różnica między sposobami przypisywania wskaźnika?

int *p, q; 
p = &q; 

czyli

int *p, *q; 
p = q; 

Powinny być takie same. Ale w moim przypadku poniżej, że pracują zupełnie inaczej:

template <typename T> 
void Tree<T>::_insert(TreeNode<T>*& tree, const T& value, const unsigned& key) 
{ 
// TreeNode<T> node(value, key); 
// if(tree == nullptr) tree = &node; 
    TreeNode<T> *node = new TreeNode<T>(value, key); 
    if(tree == nullptr) tree = node; 

    else if(key < tree->index) _insert(tree->left, value, key); 
    else if(key > tree->index) _insert(tree->right, value, key); 
    else if(key == tree->index) std::cerr << "_insert: repeating key" << std::endl; 
} 

Korzystanie pierwszy sposób (zaznaczyć jedno), funkcja nie przypisze drzewa równą węzła, natomiast drugi sposób działa dobrze.

Czy to moja wina, czy w naturalny sposób się różnią?

Odpowiedz

3

Proszę zauważyć, w pierwszym przypadku:

// TreeNode<T> node(value, key); 
// if(tree == nullptr) tree = &node; 

node jest obiekt przeznaczono na stosie.

Podczas gdy w drugim przypadku

TreeNode<T> *node = new TreeNode<T>(value, key); 
if(tree == nullptr) tree = node; 

node przeznaczono na sterty.

Różnica polega na tym, że po powrocie funkcji _insert ramka stosu jest wypełniona, a wszystkie lokalne zmienne/obiekty stają się niepoprawne, w wyniku czego wystąpią błędy pamięci.

+0

Dziękuję za odpowiedź, naprawdę to doceniam. Tak więc, jeśli rozumiem poprawnie, powodem, dla którego dostałem błąd jest to, że zmienna zadeklarowana w sterty zostanie zwolniona, gdy przekroczy zakres, tak więc w pierwszym przypadku "węzeł" zostanie zwolniony, a wskaźnik "drzewo" zostanie zresetowany do "nullptr", prawda? –

+0

@XiangyuZhang Nie, pamięć przydzielona w stercie nie zostanie zwolniona, chyba że wyraźnie to zrobisz (dlatego nie miałeś problemu w drugim przypadku). W pierwszym przypadku wskaźnik "drzewo" nie zostanie zresetowany do "nullptr", gdy ramka stosu na _insert zostanie wyskoczona, pozostanie nadal tym, czym była, ale ponieważ zwracana jest wartość _insert(), co oznacza "drzewo" jest po prostu losową pamięcią (ramka stosu następujących wywołań funkcji nadpisze przydzieloną pamięć stosu), dereferencja/dostęp do wskaźnika "drzewa" z pewnością będzie problematyczny. –

+0

@XiangyuZhang Może to pomocne przy patrzeniu na https://en.wikipedia.org/wiki/Call_stack#STACK-FRAME –

3

Nie, te dwie drogi nie powinny być takie same:

  • Pierwszy przydział p = &q jest całkowicie poprawny, ponieważ q jest rzeczywisty obiekt w pamięci, a p jest wskaźnikiem do niej
  • Drugi przypisanie p = q przypisuje wskaźnik unitialized q do p, który jest niezdefiniowanym zachowaniem.

Dlatego te dwie implementacje są różne.

Jeśli chcesz przypisać q do p, sama nazwa q musi zostać przypisana jako pierwsza. Na przykład, można przypisać new int do niego:

int *p, *q = new int; 
p = q; 

Jednak w tym przypadku równie dobrze można przypisać new int bezpośrednio p.

0
int *p, q; 
p = &q; 

Oznacza to, że p ma teraz adres, pod którym zapisana jest liczba całkowita q.

int *p, *q; 
p = q; 

W tym miejscu kopiujesz adres zapisany w wskaźniku q do p.

Powiązane problemy