2010-02-08 13 views
5

tl; dr - Czy możesz rozwinąć 4 komentarze w pierwszym fragmencie kodu poniżej? W szczególności chodzi o derefPytanie C/C++ Pytanie

Jestem długoletnim programistą Java, który chce się uczyć języka C++. Natknąłem się na this website skierowaną do programistów w mojej sytuacji.

int x, *p, *q; 
    p = new int; 
    cin >> x; 
    if (x > 0) q = &x; 
    *q = 3;     // 1. deref of possibly uninitialized ptr q 
    q = p; 
    p = new int;   // 2. potential storage leak (if x != 0 this 
          //  memory will not be returned to free storage) 
    *p = 5; 
    delete q; 
    *q = 1;     // 3. deref of deleted ptr q 
    q = p; 
    if (x == 0) delete q; 
    (*p)++;     // 4. deref of possibly dangling ptr p (if x is zero) 

Chociaż wydawało mi się, że rozumiem, jak działają wskaźniki, znajduję komentarze trudne do zrozumienia.

My take;

  1. Jesteśmy albo przypisanie x (& * q oczywiście) być 3 lub jeżeli q! = & x wtedy q ma tylko wartość, jak to było niezainicjowany i właśnie przypisany losowy kawałek pamięci na wartość 3. Nie wiem, w jaki sposób można usunąć coś, co nie zostało zainicjowane?
  2. To jest w porządku
  3. Co jest nie tak z usuwaniem usuniętego wskaźnika? Po "usuń q" jest * q bez znaczenia?
  4. Co jest nie tak z wiszącymi wskaźnikami? Czy pamięć jest realistyczna do ponownego przydzielenia, skoro ją usunęliśmy, mimo że wciąż mamy do niej wskaźnik?

Myślę, że moje podstawowe nieporozumienie dotyczy tylko deklarowania wskaźnika int, czy to również przydziela pamięć? Czy jest na stosie lub sterty?

Czy dereferencja oznacza także "odczytanie wartości pod adresem wskaźnika"? Myślę, że moje zamieszanie polega na tym, że interpretuję to jako utratę odniesienia do niektórych danych w;

int *x; 
x = new int; 
*x = 5; 
x = new int; // Dereferencing the first bit of memory allocated. 

Dzięki za ciebie cierpliwość mam nadzieję, że to sprawia, że ​​jakiś sens jako pytanie,

GAV

+0

Dobre podsumowanie wszystkich problemów, które mogą wystąpić w przypadku wskaźnika. –

+0

Z drugiej strony nazwy zmiennych uniemożliwiają przestrzeganie logiki. Prawdziwy kod nigdy nie jest zdalnie zbliżony do tego skomplikowanego (a raczej nie powinno być) –

Odpowiedz

6

Upraszczając: Wskaźnik jest adres zmiennej, której wartość może być zainteresowany w

dereferencing jest aktem dostępu do tej wartości - przez poprzedzenie (operator wyłuskania) z * do. zmienna wskaźnika. Dostęp może być dla czytania, pisania lub obu.

  1. Jeśli nie zainicjować wskaźnik do ważnego adresu (lub wartość specjalną null) - nie wiem, co to zmienna zawiera. Dereferencja spróbuje wziąć wszystko, co jest, i potraktować to jako adres. To jest niezdefiniowane zachowanie - wszystkie zakłady są wyłączone, wszystko może się zdarzyć, ale jeśli masz szczęście, otrzymasz wyjątek od pułapki/sprzętu.

  2. Dokładnie. Ponieważ p trzymał adres pamięci, którą przydzieliłeś. Brak uwolnienia zasobów systemowych powoduje wyciek. Wywołanie usuniętego wskaźnika jest takie samo, jak dereferencja niezainicjowanego wskaźnika - nie wiesz, jaką wartość może zawierać.

  3. Ponownie, UB.

  4. Prawda. Masz UB dla x == 0. Zwisające wskaźniki są niebezpieczne, ponieważ przesuwają się w najbardziej nieodpowiednim czasie, formatują dysk twardy i nigdy nie są widoczne. Niemożliwe jest uzasadnione uzasadnienie zachowania programu.

+0

+1. Zwisające wskaźniki wskazują również na pamięć, że będziesz w stanie ponownie dotrzeć. – ALOToverflow

+0

Kluczowym punktem, który trafiłeś, było drugie zdanie - nie wiedziałem, że termin "dereferencja" oznacza dostęp do wartości przechowywanej pod adresem w zmiennej. Myślałem, że oznacza to coś w rodzaju "braku odniesienia" - usunięcie ostatniego odniesienia do fragmentu pamięci bez usuwania go. Dzięki – gav

1

Odpowiedzi do listy trwa:

  1. Wartość x jest odczytywany z strumień. To może się nie powieść, pozostawiając x niezainicjowany (przechowując wartość śmieci). Tak więc zachowanie if nie jest niezawodne, a zatem może nie przypisać adresu do q. Tak więc zadanie *q = 3 zapisałoby się na losowym adresie pamięci.

  2. To nie jest w porządku! Nie ma zbierania śmieci. Pierwszy obiekt został przydzielony na stercie, a jego adres został przypisany do p. Teraz przydzielisz drugi i nadpisujesz wskaźnik, aby zachować nowy adres. Stary adres jest tracony, więc nikt nie może go usunąć przez p. Później jest delete q, który jest kopią adresu z p, ale ta instrukcja jest zależna od wartości x (która jest niewiarygodna).

  3. To nie jest wskaźnik, który został usunięty. To obiekt wskazany. Wskaźnik nadal ma wartość, która wskazuje na to, co było wcześniej ważnym obiektem, ale teraz jest to śmieci. Jeśli usuniesz zaznaczenie, twój program będzie miał "niezdefiniowane zachowanie".

  4. Tak. System wykonawczy (taki jaki jest) nie ma śledzenia wskaźników do obiektów.

2

w:

  1. przypisać wartość do q wskaźnik przez wyłuskania go z adresem do betonu pamięci see Pointer dereference doc.. Jednak ze względu na warunek if ustawienie go na adres x może wskazywać na losową komórkę pamięci (jest niezainicjowana), jeśli x <= 0.
  2. W powyższej linii ustawiamy q tak, aby wskazywał ten sam adres pamięci co p. Następnie dla p alokujesz nową pamięć. A następnie przydzielisz nową pamięć, na którą wskazuje p.
  3. Linia powyżej usunąłeś alokację dla pamięci wskazanej przez q. Ten adres pamięci jest teraz dostępny dla systemu. A potem przypisujesz wartość pamięci, która "nie należy" do ciebie.
  4. Jeśli włączysz x == 0, powrócisz do pamięci systemowej wskazanej przez q i p. I znowu próbuje użyć pamięci, która "nie należy" do ciebie. Również nie ma żadnego delete p, jeśli x != 0 - w ten sposób pamięć nie jest zwracana do systemu.
3

Dodano więcej komentarzy w linii poniżej:

int x, *p, *q; 
    p = new int; 
    cin >> x; 
    if (x > 0) q = &x; 
    *q = 3;     // <--- 1. deref of possibly uninitialized ptr q 
          // 
          // q has never been set or is set to point at a non 
          // allocated memory location (if x > 0) 
          // 
          // If x is zero q is uninitialized (has random value). 
          // Therefore de-referencing it to set what it points at 
          // to 3 has undefined behavior. 

    q = p; 
    p = new int;   // <--- 2. potential storage leak (if x != 0 this 
          //  memory will not be returned to free storage) 
          // 
          // This comment is not true. The value pointed at by p 
          // was transferred to q before p was re-assigned. Thus 
          // there is no potential memory leak. Both p and q are 
          // now valid pointers. 
    *p = 5; 
    delete q; 
    *q = 1;     // <--- 3. deref of deleted ptr q 
          // 
          // In the line above you returned the memory pointed at 
          // by q back to memory management routines (via delete). 
          // Technically q still points at the memory but you no 
          // longer own that memory and thus writing to it has 
          // undefined behavior (because the memory management 
          // system could have unloaded that chunk from virtual 
          // memory or re-used it some other way). 
    q = p; 
    if (x == 0) delete q; 
    (*p)++;     // <--- 4. deref of possibly dangling ptr p (if x is zero) 
          // 
          // q is reassinged to have the same pointer value as p. 
          // Then If x is zero we delete the pointer q (thus 
          // returning it to the memory management pool). Since p 
          // and q are the same pointer they both become invalid at 
          // point. Thus de-referencing p is undefined behavior if 
          // x is zero (because the memory was returned (via delete) 

1 Jesteśmy albo przypisanie x (& * q oczywiście) być 3 lub jeśli q = & x wtedy q ma! po prostu wartość, ponieważ była niezainicjowana i właśnie przypisaliśmy losową pamięć do wartości 3. Nie jestem pewien, w jaki sposób można usunąć coś, co nie zostało zainicjowane?

Patrz poniżej:

3 Co się dzieje z dereferencing usunięty wskaźnik? Po "usuń q" jest * q bez znaczenia?

Pamięć należy do kogoś innego (zarządzania pamięcią (lub w gwintowanych aplikacji może już zostały przesunięte do innego wątku). Zapis do pamięci, które nie należą do was niezdefiniowane zachowanie.

4 Co z błędnych odwołań? Czy pamięć opłacalne dla realokacji teraz, że zostały usunięte, nawet jeśli wciąż mamy wskaźnik do niego?

sam komentarz jako 3. (tylko dlatego, że zachowują nieprawidłowy wskaźnik robi nie znaczy nic)

Czy dereferencja oznacza po prostu "odczytanie wartości pod adresem wskaźnika"? Myślę, że moje zamieszanie polega na tym, że interpretuję to jako utratę odniesienia do niektórych danych w;

Tak: To wszystko, co oznacza.
Ale jeśli nie jesteś właścicielem tej pamięci, mogą się wydarzyć okropne rzeczy.

  • Jeżeli wskaźnik wskazuje na lokalizację w pamięci, że nie jest odwzorowane w pamięci fizycznej wtedy dostaniesz jakąś formę winy segmentacji.
  • Jeśli pamięć należy do procedur zarządzania pamięcią i zaczynasz wpisywać do niej przypadkowe wartości (np. 3 powyżej), możesz uszkodzić struktury zarządzania pamięcią, powodując awarię programu po zajętym zarządzaniu pamięcią.
  • Jeśli pamięć należy do stosu i zaczniesz zapisywać wartości losowe (np. 3 powyżej), możesz potencjalnie zastąpić inną zmienną losową lub adres powrotu używany, gdy funkcja zostanie zakończona.
2

Jest to trudny temat, jeśli nie jesteś świadomy tego, co dzieje się "pod maską". C++ jest raczej językiem programowania "bare-metal" i rzadko czyni wszystko bezpiecznym.

Patrząc na kodzie dostaw i zaznaczonymi punktami zainteresowania:

Pierwsza linia: int x, *p, *q; który deklaruje i definiuje kilka zmiennych na stosie, ale ich nie zainicjować. Ani C++ nie pamięta, w czasie wykonywania, że ​​tak się stało. Możesz otrzymać kilka ostrzeżeń podczas kompilacji, ale poza tym musisz pilnować rzeczy w swojej głowie.

Tak więc, w linii 1. nie wiemy, czy q jest &x czy nie. Jeśli q nie zostało zainicjalizowane, może wskazywać gdziekolwiek, ale C++ tego nie wie i spróbuje zapisać gdzieś wartość 3 do jakiejś pamięci. Odpowiedź na to pytanie brzmi, że C++ nie śledzi wartości wskaźnika.

W linii 3., ponownie C++ próbuje zapisać wartość całkowitą do pewnej części pamięci, ale tym razem jej na stercie iw programie wielowątkowym mogły zostać ponownie przydzielone do czasu wykonania linii. Tak więc, niestety, q i *q zachowują znaczenie, ale jest mało prawdopodobne, aby oznaczało to, czego chcemy.

Na linii 4. jej ten sam problem jak 3., ale tylko wtedy, gdy (x==0), i tak jak mówisz, pamięć mogła zostać ponownie przydzielone chociaż nie usunął go.

Zgłaszanie wskaźnika int przydziela tylko pamięć dla wskaźnika samemu i na stosie.

Dereferencja wskaźnika oznacza dostęp do pamięci, do odczytu lub zapisu, gdzie wskazuje wskaźnik.

W odniesieniu do drugiego kawałka kodu:

int *x;   <-- Declares a pointer and allocates it on the stack 
x = new int; <-- Allocate a new int on the heap and remember its address in x 
*x = 5;   <-- Overwrite the new int on the heap. 
x = new int; <-- Another allocation and remember its address in x 
        Now we have forgotten where the first allocation was 

oznacza nieprawidłowego odczytu lub zapisu do pamięci, że punkty wskaźnik do. Jeśli po prostu czytasz lub piszesz do samego wskaźnika, to zawsze jest on bezpieczny. Dlatego możesz omijać wskaźnik null i wpadać w kłopoty tylko wtedy, gdy po raz pierwszy został z niego usunięty.