2009-05-07 14 views
8

Ho un puntatore a una determinata classe. Diciamo, per esempio, il puntatore è:Cosa determina cosa viene scritto in un puntatore C++ quando viene chiamato delete?

0x24083094 

che punta puntatore a:

0x03ac9184 

che è la tabella di funzione virtuale della mia classe. Questo ha senso per me. In windbg, tutto sembra corretto.

Cancellare detto puntatore. Ora a 0x24083094 è:

0x604751f8 

Ma non è un po 'di spazzatura a caso, tale indirizzo è messo lì ogni volta, è costantemente 0x604751f8! Tanto che posso effettivamente usare quell'indirizzo per determinare se quel puntatore è stato cancellato, tra le esecuzioni della mia applicazione!

Ma perché? Come si determina che 0x604751f8 debba essere scritto lì?

Per la cronaca, io sto usando le finestre, edificio in Visual Studio 2003.

so che non posso contare su tale valore viene impostato, anche se non sembra essere coerente, ma posso contare su è diverso? Ad esempio, 0x03ac9184 non sarà a 0x24083094 se il puntatore viene eliminato, giusto? Cosa viene messo lì? Potrebbe essere qualsiasi cosa, ma 0x03ac9184 non ci sarà sicuramente (altrimenti potrei ancora chiamare metodi, dato che è la tabella delle funzioni virtuali). Ho ragione?

Mi sento come se avessi una risposta. non posso fare affidamento su nulla dopo che è stato cancellato. Forse un po 'di background aiuterà le persone a vedere da dove vengo. In sostanza, sto cercando di correggere un bug in cui un puntatore viene eliminato da sotto di me. È una lunga storia, non entrerò nei dettagli.

Fondamentalmente, sto cercando di rilevare che sono in questa situazione, quindi posso uscire con garbo dalla mia funzione. Suppongo che il modo più semplice e migliore sia quello di capire chi possiede questo puntatore e chiedergli se qualcosa è cambiato. Quindi ho intenzione di implementare una soluzione del genere. Evita qualsiasi hacker di cancellazione C++ di cui stavo discutendo.

Tuttavia, la cosa interessante è che nel nostro codice abbiamo una classe chiamata "BogusObject" che agisce essenzialmente come un vassoio che cattura persone che accidentalmente si distolgono dagli oggetti liberati. Fondamentalmente, agganciamo le nostre proprie funzioni di cancellazione e bash la classe BogusObject nel vtable di qualsiasi classe liberata.

Quindi se qualcuno chiama qualcosa riceve un bel messaggio che dice qualcosa all'effetto di "hey, qualcosa è sbagliato amico". Questo sta accadendo nel mio caso. Ad esempio, 0x604751f8+(someoffset) si trova nella classe BogusObject. Ma non usiamo più BogusObject! Non è letteralmente impostato ovunque (anche i collegamenti correttamente se rimuovo completamente la classe BogusObject), eppure continuo a ricevere il messaggio che dice che qualcosa non va! Ma ora sono dell'opinione che sia una coincidenza.

Per qualche motivo il runtime sta mettendo il valore 0x604751f8 in questo puntatore quando viene eliminato, e ciò accade solo per corrispondere con una classe che ha lo scopo di catturare situazioni come questa!

+1

Perché non si imposta un punto di interruzione dati sul puntatore (0x24083094) e si vede quando viene scritto 0x604751f8? Quando il debugger si rompe, esegui la scansione della pila per vedere chi ti ha chiamato e da dove proviene il valore. –

+0

sì, l'ho fatto. Viene modificato dopo essere tornato da myModule! MyClass :: 'vettore che elimina il distruttore '. Sono entrato in ogni linea tra quando viene chiamato delete e quando cambia. In effetti, ho un registro di Windbg che mostra questo, con alcuni cambiamenti di esso. Tuttavia, ci sono molte cose della compagnia, quindi ho bisogno di scrub prima di condividere. – pj4533

risposta

16

Niente nello standard determina ciò che viene scritto lì.Visual Studio (almeno in modalità debug) spesso scriverà valori sentinella dappertutto per aiutare a catturare i bug in anticipo.

Questo valore non è qualcosa su cui puoi fare affidamento, ma se trovi questo valore nel tuo programma misteriosamente, puoi supporre che da qualche parte stai facendo riferimento alla memoria cancellata. Vedere this answer per un elenco di valori in un unico compilatore.

È anche possibile che sia un puntatore a elenco libero, che punta al prossimo pezzo di memoria libera. La maggior parte degli allocatori di memoria mantengono la loro memoria libera in un elenco collegato di ordinamenti, utilizzando la memoria disponibile che stanno tracciando per archiviare i dati di tracciamento.

In ogni caso, NON si deve usare quel valore del puntatore per tutto ciò che si vuole continuare a lavorare, a meno che non si richiami microsoft e si ottenga della documentazione che dica perché quel valore è quello che è, e si assicura che non lo faccia modificare. E anche allora, sappi che il tuo codice è ora legato al comportamento di un compilatore. In C++, l'accesso alla memoria non allocata è indefinito e malvagio.

Modifica: Non si può nemmeno contare su quel valore che cambia dopo un'eliminazione. Non c'è nulla che dice che un compilatore debba modificare i dati su delete.

+0

Sì, lo so di FEEEFEEE e quant'altro. Ma perché la coerenza nel mio caso per farlo SEMPRE mettere 0x604751f8 in quel punto? – pj4533

+3

0x604751f8 è probabile che sia un puntatore a qualcosa (forse un altro blocco libero). Ma potrebbe anche essere un mucchio di bitflags o una firma –

+5

o spazzatura (non c'è nulla che dice che la spazzatura non può essere coerente). –

2

La semplice chiamata dell'operatore di eliminazione per un puntatore non significa che la memoria "eliminata" verrà cancellata. Chiamerà solo il distruttore degli oggetti eliminati e segnerà la memoria heap allocata come deallocata. (questo è il comportamento predefinito dell'operatore di cancellazione).

Se è necessario cancellare il contenuto della memoria quando si elimina, è necessario ignorare l'operatore di cancellazione.

+0

Giusto, ma QUALCOSA altro deve accadere, perché la memoria cambia. Capisco che non lo cancelli .... ma lo rende costantemente 0x604751f8. Perché? – pj4533

+0

Stai controllando subito dopo l'istruzione delete? In tal caso, il distruttore modifica il puntatore o il puntatore viene modificato da un altro thread. – Jonatan

+0

Cambia quando viene compilato in modalità Debug.AFAIK Le funzioni di deallocation dietro delete cambiano il contenuto della memoria deallocated per "contrassegnare" la memoria come libera, a scopo di debug. Ma non nel target di Release, per motivi di prestazioni ... –

4

Uno si elimina un oggetto la memoria che stava utilizzando viene rimesso nel negozio gratuito (heap). Ovviamente il negozio gratuito avrà le sue strutture dati (e possibilmente il debug dei dati) che si applicherà a quella memoria.

Qual è il valore particolare che stai guardando? Potrebbe essere quasi tutto.

+0

Ancora di più, è probabilmente un accesso disallineato nel mezzo di una struttura di record dell'heap. – Joshua

2

Come diceva Josh, ci sono diversi valori che possono essere inseriti, per semplificare la vita del debugger con build di debug. Questi sono specifici del compilatore e in nessun modo possono essere considerati affidabili. In una versione build, credo che il comportamento predefinito della maggior parte dei compilatori C++ sia quello di non fare nulla con la memoria che viene liberata, quindi, fino a che lo spazio indirizzo non viene nuovamente allocato, il contenuto sarà fondamentalmente ciò che è mai esistito prima, naturalmente, tu non dovresti mai fare affidamento su questo.

2

C'è quasi sicuramente un significato interno specifico al valore che appare ogni volta che si cancella l'oggetto.

Tuttavia, potrebbe cambiare con la versione successiva di Visual C++ e sarà sicuramente diverso su altri compilatori di fornitori.

Il fatto che sembra essere lo stesso ogni volta che si elimina un oggetto non significa nulla di utile. Né può essere potenzialmente utile. Supponendo che tu abbia trovato un modo per approfittarne, sarebbe un trucco spaventoso farlo e te ne pentiresti alla fine.

Cerca di non pensare!

+0

Potrebbe essere utile come aiuto per il debug. – florin

+0

@florin, a meno che non ti piaccia il debug statistico, è una cattiva idea. – Joshua

2

Come detto in precedenza da Michael Burr, il ricordo torna al negozio gratuito. Alcuni negozi gratuiti sono implementati come liste collegate, con il puntatore -> posizionato all'inizio del buffer libero. È possibile che il numero magico che stai vedendo (0x604751f8) sia la guardia di fine lista.È possibile controllare eseguendo il seguente esperimento:

Foo* f = new Foo(); 
Bar* b = new Bar(); 

// make a note of the values of f and b _pointers_ 

delete b; // check that b points now to 0x604751f8 
delete f; // check that f points now to 0x604751f8 

// now check that does b point to; it might point to f! 

farci sapere quello che si trova!

2

Molto probabilmente questo valore del puntatore è il vtable della classe base. Quando un distruttore per una classe derivata viene eseguito, dopo aver completato il suo corpo normale, "riordina" la memoria come tipo di base (in pratica, scrive il puntatore vtable della classe base nell'oggetto) e quindi chiama il distruttore della classe base.

Si noti che questo comportamento è un dettaglio interno dell'implementazione del supporto C++ del runtime del compilatore, quindi altri compilatori (o versioni future dello stesso compilatore) potrebbero fare qualcosa di completamente diverso. Ma questo 'convertire vtable in classe base e chiamare il distruttore della classe base' è abbastanza comune e risale all'implementazione originale di C++

2

Il programma sta cercando di dirti qualcosa. Una data, un numero di telefono, chi lo sa?

Ora sul serio, questo è non nominati, totalmente implementazione-dipendente, e, naturalmente, cercare di risolvere il riferimento che il puntatore dopo delete porterebbe ad un comportamento indefinito. Quindi, in breve, chi se ne importa?

2

No, non si può fare affidamento sul fatto che sia impostato. Non puoi nemmeno fare affidamento sul fatto che sia diverso.

I gestori di heap di MS-DOS hanno spesso consentito l'utilizzo della memoria liberata fino alla successiva chiamata a malloc. Nuovo ed elimina in quel periodo chiamato malloc e gratuito.

In questi giorni, la maggior parte dei responsabili dell'heap è ragionevole per restituire memoria al sistema operativo, il che significa che non si può nemmeno contare sul fatto che sia leggibile! Anche uno che lo consente (glibc ha una modalità bwd-compat che lo consente) sei soggetto a condizioni di competizione.

Inoltre, delete è autorizzato a cambiare il puntatore su NULL se è un lvalue.

Dopo aver chiamato delete, non pensare nemmeno al dereferenziamento del puntatore.

0

È possibile accedere al codice sorgente CRT in Visual Studio. Potresti dare un'occhiata. Ho fatto una volta per capire meglio un bug che avevo.

Problemi correlati