Domanda

Come tutti sanno, il Visual C++ il runtime contrassegna i blocchi di memoria non inizializzati o appena liberati con speciali marcatori diversi da zero.Esiste un modo per disabilitare completamente questo comportamento senza impostare manualmente tutta la memoria non inizializzata su zero?Da allora sta causando il caos con i miei controlli validi e non nulli 0xFEEEFEEE != 0.

Hrm, forse dovrei spiegarmi un po' meglio.Creo e inizializzo una variabile (tramite new) e tutto va bene.Quando lo libero (tramite Elimina), imposta il puntatore su 0xFEEEFEEE invece di NULL.Quando inserisco un controllo corretto per NULL, come dovrebbero fare tutti i buoni programmi che gestiscono la propria memoria, mi vengono in mente problemi come 0xFEEEFEEE passa a NULL controlla senza problemi.Esiste un buon modo, oltre a impostare manualmente tutti i puntatori su NULL quando li elimini, per rilevare quando la memoria è già stata liberata?Preferirei non usarlo Aumento semplicemente perché non voglio un sovraccarico, per quanto piccolo possa essere, poiché è l'unica cosa per cui utilizzerei Boost.

È stato utile?

Soluzione

Non è responsabilità di delete per reimpostare tutti i puntatori all'oggetto su NULL.Inoltre non dovresti modificare il riempimento di memoria predefinito per il runtime DEBUG di Windows e dovresti usare qualcosa di simile boost::shared_ptr<> per i puntatori in ogni caso.

Detto questo, se lo vuoi davvero sparati ai piedi puoi.

Puoi modifica IL riempimento predefinito per le finestre Runtime di DEBUG utilizzando un hook allocatore come questo.Funzionerà solo su oggetti allocati HEAP!

int main(int argc,char** arv)
{
  // Call first to register hook    
  _CrtSetAllocHook(&zero_fill);
  // Do other stuff
  malloc(100);
}


int zero_fill(int nAllocType, 
              void* pvData, 
              size_t nSize,
              int nBlockUse, 
              long lRequest, 
              const unsigned char *szFileName, 
              int nLine )
{
  /// Very Importaint !! 
  /// infinite recursion if this is removed !!
  /// _CRT_BLOCK must not do any thing but return TRUE
  /// even calling printf in the _CRT_BLOCK will cause
  /// infinite recursion
  if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );
  switch(nAllocType)
  {
  case _HOOK_ALLOC:
  case _HOOK_REALLOC:
    // zero initialize the allocated space.
    memset(pvData,0,nSize);
    break;
  case _HOOK_FREE:
    break;
  }
  return TRUE;
}

Altri suggerimenti

Quando crei un puntatore, inizializzalo esplicitamente su NULL.Allo stesso modo dopo a delete.A seconda del valore dei dati non inizializzati (tranne in alcuni casi specifici) si verificano problemi.

Puoi risparmiarti un sacco di mal di testa usando una classe di puntatori intelligenti (come boost::shared_ptr) che si occuperà automaticamente se un puntatore è inizializzato o meno.

Il comportamento di VC++ non dovrebbe causare danni a nessuno valido controlla che puoi fare.Se vedi 0xfeeefeee allora non hai scritto nella memoria (o l'hai liberata), quindi non dovresti comunque leggere dalla memoria.

Se stai leggendo la memoria non inizializzata, i tuoi controlli sicuramente non sono "validi".La memoria è liberata.Potrebbe già essere utilizzato per qualcos'altro.Non è possibile fare alcuna ipotesi sul contenuto della memoria non inizializzata in C/C++.

Java (e C#, credo) garantiranno che la memoria allocata venga azzerata prima dell'uso e, naturalmente, la garbage collection ti impedisce di vedere la memoria liberata.Ma questa non è una proprietà dell'heap C, che espone direttamente la memoria.

Se si compila in modalità di rilascio anziché in modalità debug, il runtime non riempirà affatto la memoria non inizializzata, ma non sarà comunque costituito da zeri.Tuttavia, dovresti non dipende da questo comportamento: dovresti inizializzare esplicitamente la memoria tu stesso con memset(), ZeroMemory() o SecureZeroMemory(), oppure impostare un flag da qualche parte che indichi che la memoria non è ancora inizializzata.La lettura della memoria non inizializzata comporterà un comportamento indefinito.

Tu dici:

Creo e inizializzo una variabile (tramite new) e tutto va bene.Quando lo libero (tramite Elimina), imposta il puntatore su 0xFEEEFEEE anziché NULL.Quando inserisco un controllo corretto per NULL, come dovrebbero fare tutti i buoni programmi che gestiscono la propria memoria, riscontro problemi poiché 0xFEEEFEEE supera un controllo NULL senza problemi.

Anche le routine dell'heap di debug di MSVC non modificheranno il valore di puntatore stai eliminando: il valore del puntatore che stai eliminando non cambierà (nemmeno su NULL).Sembra che tu stia accedendo a un puntatore che appartiene all'oggetto che hai appena eliminato, il che è un bug, chiaro e semplice.

Sono abbastanza sicuro che ciò che stai cercando di fare coprirà semplicemente un accesso alla memoria non valido.Dovresti pubblicare uno snippet di codice per mostrarci cosa sta realmente accadendo.

@Jeff Hubbard (commento):

Questo in realtà mi fornisce inavvertitamente la soluzione che desidero:Posso impostare pvData su NULL su _HOOK_FREE e non incorrere in problemi con 0xFEEEFEEE per l'indirizzo del mio puntatore.

Se questo funziona per te, significa che stai leggendo la memoria liberata quando stai testando il puntatore NULL (cioè, il puntatore stesso risiede nella memoria che hai liberato).

Questo è un errore.

La "soluzione" che stai utilizzando è semplicemente nascondere, non correggere, il bug.Quando la memoria liberata viene allocata a qualcos'altro, all'improvviso utilizzerai il valore sbagliato come puntatore alla cosa sbagliata.

Questa è in realtà una funzionalità molto interessante in VC++ (e credo in altri compilatori) perché ti consente di vedere la memoria non allocata per un puntatore nel debugger.Ci penserò due volte prima di disabilitare quella funzionalità.Quando elimini un oggetto in C++ dovresti impostare il puntatore su NULL nel caso in cui qualcosa in seguito tenti di eliminare nuovamente l'oggetto.Questa funzione ti consentirà di individuare i luoghi in cui hai dimenticato di impostare il puntatore NULL.

Se funziona in modalità di rilascio, è per pura fortuna.

Mike B ha ragione nel ritenere che la correzione del debug nasconda un bug.Nella modalità di rilascio viene utilizzato un puntatore che è stato liberato ma non impostato su NULL, e la memoria a cui punta è ancora "valida".Ad un certo punto in futuro, le allocazioni di memoria cambieranno, o l'immagine della memoria cambierà, o qualcosa farà sì che il blocco di memoria "valido" diventi "non valido".A quel punto, la build di rilascio inizierà a fallire.Passare alla modalità debug per individuare il problema sarà inutile, perché la modalità debug è stata "risolta".

Penso che siamo tutti d'accordo sul fatto che il seguente codice non dovrebbe funzionare.

char * p = new char[16];     // 16 bytes of random trash
strcpy(p, "StackOverflow");  // 13 characters, a '\0' terminator, and two bytes of trash
delete [] p;                 // return 16 bytes to the heap, but nothing else changes;

if (p != NULL)               // Why would p be NULL?  It was never set to NULL
    ASSERT(p[0] == 'S');     // In debug, this will crash, because p = 0xfeeefeee and 
                             // dereferencing it will cause an error.
                             // Release mode may or may or may not work, depending on
                             // other memory operations

Come ha detto quasi ogni altro utente, i puntatori dovrebbero essere impostati su NULL dopo aver chiamato delete.Sta a te decidere se farlo da solo o utilizzare boost o qualche altro wrapper o anche la macro in questo thread.

Quello che sta succedendo è che il mio codice si arresta in modo anomalo sotto una raccolta di debug, ma riesce sotto una raccolta di rilascio.

La build di rilascio si bloccherà sul computer del cliente.Lo fa sempre.

L'ho controllato sotto un debugger e i miei puntatori si stanno impostato su 0xfeeefeee dopo aver chiamato elimina su di loro.

Puntatori non vengono modificati dopo aver chiamato delete su di essi.È la memoria a cui puntano che viene impostata su 0xfeeefeee, 0xfeeefeee, ..., 0xfeeefeee.

Se noti che il tuo programma legge i dati dalla memoria liberata (che è convenientemente indicato dal pattern 0xfeeefeee nella build DEBUG), hai un bug.

@[Jeff Hubbard]:

Quello che sta succedendo è che il mio codice si blocca in una compilazione di debug, ma riesce in una compilazione di rilascio.L'ho controllato in un debugger e i miei puntatori vengono impostati su 0xFEEEFEEE dopo aver chiamato cancella su di loro.Ancora una volta, lo stesso codice al momento del rilascio non si arresta in modo anomalo e si comporta come previsto.

Questo è un comportamento molto strano: sono ancora convinto che probabilmente ci sia un bug latente nascosto dal file _CrtSetAllocHook() soluzione alternativa.

IL 0xFEEEFEEE la firma viene utilizzata dal gestore heap del sistema operativo per indicare la memoria liberata (vedere http://www.nobugs.org/developer/win32/debug_crt_heap.html).Per caso puoi pubblicare del codice di riproduzione e indicare esattamente quale versione del compilatore stai utilizzando?

Sono abbastanza sicuro che non puoi disabilitare l'impostazione predefinita di Visual Studio qui e, anche se lo facessi, il valore sarebbe semplicemente quello che era in memoria prima che la memoria fosse allocata.

È meglio prendere l'abitudine di impostarli su 0 in primo luogo, sono solo 2 personaggi extra.

int *ptr=0;

Puoi anche usare la macro NULL, che è definita come 0 (ma non è quella predefinita, quindi fai attenzione con le definizioni multiple quando includi cose come windows.h e definiscile tu stesso!

se stai usando malloc, non inizializza la memoria su nulla.ottieni qualunque cosa.se vuoi allocare un blocco e inizializzarlo su 0, usa 'calloc' che è come malloc solo con l'inizializzazione (un parametro di dimensione dell'elemento che imposti su 1 se vuoi emulare malloc).dovresti leggere calloc prima di usarlo poiché presenta alcune lievi differenze.

http://wiki.answers.com/Q/What_is_the_difference_between_malloc_and_calloc_functions

Perché non creare il tuo #define e prendere l'abitudine di usarlo?

Cioè.

#define SafeDelete(mem) { delete mem; mem = NULL; }
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }

Ovviamente puoi chiamarlo come preferisci.deleteZ, deletesafe, qualunque cosa ti trovi a tuo agio.

Potresti anche creare un gestore della memoria.Quindi è possibile sovrascrivere nuovo ed eliminare per estrarre/rimettere un mandrino di memoria preallocato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top