2010-02-08 18 views
5

tl; dr - Potresti espandere i 4 commenti nel primo snippet di codice qui sotto? In particolare, ciò che si intende è derefPuntatore C/C++ Domanda

Sono uno sviluppatore Java da molto tempo che desidera imparare C++. Mi sono imbattuto nello this website per gli sviluppatori nella mia situazione.

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) 

Anche se ho pensato ho capito come funzionano i puntatori, sto trovando i commenti difficile da capire.

Il mio take;

  1. Ci sia assegnando x (& * q ovviamente) per essere 3 o se q! = & x allora q ha un valore come lo è stato inizializzato e abbiamo appena assegnato un pezzo casuale di memoria alla valore 3. Non sono sicuro di come sia possibile annullare la pubblicazione di qualcosa che non è stato inizializzato?
  2. Questo va bene
  3. Cosa c'è che non va nel dereferenziamento di un puntatore cancellato? Dopo 'cancellare q' è * q senza significato?
  4. Cosa c'è di sbagliato con i puntatori penzolanti? La memoria è utilizzabile per la riallocazione ora che l'abbiamo cancellata anche se abbiamo ancora un puntatore?

Penso che il mio equivoco di base riguardi solo la dichiarazione di un puntatore int, anche questo alloca la memoria? È in pila o nel mucchio?

Inoltre dereference significa semplicemente "leggere il valore all'indirizzo dell'indicatore"? Penso che la mia confusione sia che lo interpreto come un riferimento ad alcuni dati come in;

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

Grazie per la pazienza Mi auguro che questo fa un certo senso come una domanda,

Gav

+0

Buon riepilogo di tutti i problemi che possono verificarsi con un puntatore. –

+0

D'altra parte, i nomi delle variabili rendono impossibile seguire la logica. Il codice reale non è mai lontanamente vicino a questo complicato (o meglio, non dovrebbe essere) –

risposta

6

In poche parole: Un puntatore è un indirizzo di una variabile i cui valori si può essere interessati a

Dereferenziare è l'atto di accesso a tale valore - anteponendo il (operatore dereferenziazione) * al. variabile del puntatore. L'accesso può essere per la lettura, la scrittura o entrambi.

  1. Se non si inizializza un puntatore a un indirizzo valido (o il valore NULL speciale) - non si sa che cosa variabile contiene. Il dereferenziamento proverà a prendere qualsiasi cosa ci sia e trattarla come un indirizzo. Questo è comportamento indefinito - tutte le scommesse sono spente, qualsiasi cosa potrebbe accadere, anche se se si è fortunati si finirà con un'eccezione trap/hardware.

  2. Esattamente. Perché, p teneva l'indirizzo di un po 'di memoria che hai assegnato. Non liberare risorse di sistema causa una perdita. Dereferenziare un puntatore cancellato equivale a dereferenziare un puntatore non inizializzato: non si sa quale valore possa contenere.

  3. Ancora, UB.

  4. Vero. Hai UB per x == 0. I puntatori a spirale sono pericolosi perché si insinuano nel momento più inopportuno, formattano il disco rigido e non si vedono più. Diventa impossibile giustificare ragionevolmente come si comporterà il tuo programma.

+0

+1. Puntatori penzolanti puntano anche alla memoria che non sarai in grado di raggiungere nuovamente. – ALOToverflow

+0

Il punto chiave che hai colpito era la seconda frase: non sapevo che il termine dereferenziamento significasse accedere al valore memorizzato all'indirizzo nella variabile. Pensavo significasse qualcosa come "unreference" - rimuovendo l'ultimo riferimento a un pezzo di memoria senza cancellarlo. Grazie – gav

1

risposte al vostro prende:

  1. Il valore di x viene letto da un flusso. Questo potrebbe fallire, lasciando x non inizializzato (memorizzando un valore di posta indesiderata). Quindi il comportamento di if non è affidabile e quindi non può assegnare un indirizzo a q. Quindi l'assegnazione *q = 3 dovrebbe scrivere in un indirizzo di memoria casuale.

  2. Questo non va bene! Non c'è garbage collection. Il primo oggetto è stato allocato sull'heap e il suo indirizzo è stato assegnato a p. Ora si assegna un secondo e si sovrascrive il puntatore per mantenere il nuovo indirizzo. Il vecchio indirizzo è perso, quindi nessuno può cancellarlo tramite p. Più tardi c'è uno delete q, che è una copia dell'indirizzo da p, ma tale affermazione dipende dal valore di x (che è inaffidabile).

  3. Non è il puntatore che è stato cancellato. È l'oggetto indicato. Il puntatore contiene ancora un valore che punta a quello che era un oggetto valido, ma ora è spazzatura. Se lo annulli, il tuo programma avrà un "comportamento indefinito".

  4. Sì. Il sistema di runtime (così com'è) non ha traccia dei puntatori agli oggetti.

2

A:

  1. si assegna un valore di a q puntatore dereferenziazione da indirizzo a ricordo concreto see Pointer dereference doc.. Tuttavia a causa di una condizione if che lo imposta all'indirizzo di x, è possibile puntare a una cella di memoria casuale (non inizializzata) se x <= 0.
  2. Nella riga sopra riportata, impostare q in modo che punti allo stesso indirizzo di memoria come p. Quindi per p si assegna nuova memoria. E quindi si assegna nuova memoria a p punti.
  3. Riga sopra l'allocazione rimossa per la memoria indicata da q. Questo indirizzo di memoria è ora disponibile per il sistema da utilizzare. E dopo ciò assegni un valore alla memoria che non "ti appartiene".
  4. Se x == 0 si ritorna alla memoria di sistema indicata da q e p. E di nuovo cercando di usare la memoria che non "ti appartiene". Inoltre non c'è delete p se x != 0 - quindi la memoria non viene restituita al sistema.
3

aggiunto più commenti in linea di seguito:

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 Stiamo sia assegnando x (& * q ovviamente) per essere 3 o se q = & x allora q ha una! solo valore come non era inizializzato e abbiamo appena assegnato un pezzo di memoria casuale al valore 3. Non sono sicuro di come si possa annullare qualcosa che non è inizializzato?

Vedi sotto:

3 Cosa c'è di sbagliato con dereferenziazione un puntatore cancellato? Dopo 'cancellare q' è * q senza significato? comportamento

La memoria appartiene a qualcun altro (gestione della memoria (o in un'applicazione filettato può già essere riassegnato ad un altro thread). Salvare nella memoria che non appartiene a te è indefinito.

4 Cosa c'è di sbagliato con i puntatori penzolanti? è la memoria valida per la riassegnazione, ora che abbiamo eliminato anche se abbiamo ancora un puntatore ad esso?

Stessa osservazione 3. (solo perché si mantiene un puntatore non valido fa non significa niente)

Inoltre dereference significa semplicemente "leggere il valore all'indirizzo dell'indicatore"? Penso che la mia confusione sia che lo interpreto come un riferimento ad alcuni dati come in;

Sì: questo è tutto ciò che significa.
Ma se non si possiede quel ricordo, le cose brutte possono accadere.

  • Se un puntatore punta a una posizione nella memoria che non è mappata alla memoria fisica, si otterrà qualche forma di errore di segmentazione.
  • Se la memoria appartiene alle routine di gestione della memoria e si inizia a scrivere valori casuali (come sopra 3), è possibile corrompere le strutture di gestione della memoria causando così il crash del programma quando si verifica una gestione della memoria.
  • Se la memoria appartiene allo stack e si inizia a scrivere valori casuali (come 3 sopra), è possibile sovrascrivere potenzialmente un'altra variabile casuale o l'indirizzo di ritorno utilizzato quando una funzione viene chiusa.
2

Questo è un argomento difficile se non si è consapevoli di ciò che sta accadendo "sotto il cofano". Il C++ è piuttosto un linguaggio di programmazione "bare metal" e raramente rende tutto sicuro.

Guardando il codice fornite ei punti segnati di interesse:

La prima linea ha: int x, *p, *q; che dichiara e definisce alcune variabili sullo stack, ma non li inizializza. Né C++ ricorda, a run-time, che questo è successo. Potresti ricevere degli avvertimenti in fase di compilazione, ma altrimenti devi tenere traccia delle cose nella tua testa.

Quindi, alla riga 1. non sappiamo se q è &x oppure no. Se q non è stato inizializzato, allora potrebbe puntare ovunque, ma C++ non lo sa e proverà a scrivere il valore 3 su qualche pezzo di memoria da qualche parte. La risposta a questo punto è che C++ non tiene traccia dei valori del puntatore.

Alla riga 3., di nuovo il C++ sta tentando di scrivere un valore intero su un pezzo di memoria, ma questa volta sull'heap e in un programma multithread potrebbe essere stato riallocato nel momento in cui viene eseguita questa riga. Quindi, sfortunatamente, q e *q mantengono un significato, ma è improbabile che significhi ciò che vogliamo.

Adiacente 4. la sua lo stesso problema di 3., ma solo se (x==0), e sì come dici tu, la memoria avrebbe potuto essere riallocati anche se abbiamo eliminato.

La dichiarazione di un puntatore int assegna solo memoria per il puntatore da solo e in pila.

Il deviraggio di un puntatore indica l'accesso alla memoria, per la lettura o la scrittura, a cui punta il puntatore.

Per quanto riguarda il secondo pezzo di codice:

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 

risoluzione del riferimento significa leggere o scrivere la memoria che punta il puntatore a. Se leggi o scrivi sul puntatore stesso, è sempre sicuro. Questo è il motivo per cui è possibile aggirare un puntatore null e mettersi nei guai solo quando viene cancellato per la prima volta.