Question

Comme chacun le sait, les marques Visual C ++ non initialisées ou libérées blocs de mémoire avec des marqueurs spéciaux non nuls. Existe-t-il un moyen de désactiver complètement ce comportement sans définir manuellement toutes les mémoires non initialisées sur des zéros? Cela cause des ravages avec mes vérifications valides non nulles, puisque 0xFEEEFEEE != 0.

Hrm, je devrais peut-être expliquer un peu mieux. Je crée et initialise une variable (via new), et tout se passe bien. Lorsque je le libère (via delete), il positionne le pointeur sur 0xFEEEFEEE au lieu de NULL. Lorsque j'insère un contrôle approprié pour <=>, comme tous les bons programmes qui gèrent leur propre mémoire, des problèmes se posent lorsque <=> passe un <=> contrôle sans problèmes. Existe-t-il un bon moyen, autre que de définir manuellement tous les pointeurs sur <=> lors de leur suppression, pour détecter le moment où la mémoire a déjà été libérée? Je préférerais ne pas utiliser Boost simplement parce que je ne veux pas de surcharge Même si elle est petite, c’est la seule chose pour laquelle je me servirais de Boost.

Était-ce utile?

La solution

Ce n'est pas la responsabilité de delete de réinitialiser tous les pointeurs vers l'objet sur NULL. De même, vous ne devez pas modifier le remplissage de mémoire par défaut pour l'exécution de Windows DEBUG et vous devez utiliser un paramètre tel que boost::shared_ptr<> pour les pointeurs de toute façon.

Cela dit, si vous voulez vraiment vous tirer une balle dans le pied , vous le pouvez.

Vous pouvez modifier le remplissage par défaut du runtime DEBUG à l'aide d'un hook d'allocateur comme celui-ci. Cela ne fonctionnera que sur les objets alloués 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;
}

Autres conseils

Lorsque vous créez un pointeur, explicitement, initialisez-le à NULL. De même après un delete. Selon la valeur des données non initialisées (sauf dans quelques cas spécifiques), des problèmes se posent.

Vous pouvez éviter beaucoup de maux de tête en utilisant une classe de pointeur intelligent (telle que boost::shared_ptr ) qui déterminera automatiquement si un pointeur est initialisé ou non.

Le comportement de

VC ++ ne devrait pas causer de dégâts lors de toute vérification valide que vous pouvez effectuer. Si vous voyez 0xfeeefeee, vous n'avez pas écrit (ou l'avez libéré) dans la mémoire. Vous ne devriez donc pas lire dans la mémoire.

Si vous lisez de la mémoire non initialisée, vos vérifications ne sont certainement pas & "valides &"; La mémoire est libérée. Il pourrait déjà être utilisé pour autre chose. Vous ne pouvez faire aucune hypothèse sur le contenu de la mémoire non initialisée en C / C ++.

Java (et C #, je crois) garantira que la mémoire allouée est mise à zéro avant utilisation, et bien sûr, le garbage collection vous empêche de voir la mémoire libérée. Mais ce n’est pas une propriété du tas C, qui expose directement la mémoire.

Si vous construisez en mode de publication au lieu du mode de débogage, le moteur d’exécution ne remplit pas du tout la mémoire non initialisée, mais ce ne sera toujours pas un zéros. Cependant, vous ne devriez pas dépendre de ce comportement - vous devez soit explicitement initialiser vous-même la mémoire avec memset (), ZeroMemory () ou SecureZeroMemory (), ou définir un indicateur quelque part indiquant que la mémoire n'est pas encore initialisé. La lecture de mémoire non initialisée entraînera un comportement indéfini.

Vous dites:

  

Je crée et initialise une variable (via new), et tout se passe bien. Lorsque je le libère (via delete), il place le pointeur sur 0xFEEEFEEE au lieu de NULL. Lorsque j'insère un contrôle correct pour NULL, comme tous les bons programmes qui gèrent leur propre mémoire, des problèmes surviennent lorsque 0xFEEEFEEE passe un contrôle NULL sans problème.

Même les routines de débogage de MSVC ne modifieront pas la valeur du pointeur que vous supprimez - la valeur du pointeur que vous supprimez ne changera pas (même en NULL). On dirait que vous accédez à un pointeur appartenant à l'objet que vous venez de supprimer, ce qui est un bug, purement et simplement.

Je suis à peu près sûr que ce que vous essayez de faire ne fera que dissimuler un accès invalide à la mémoire. Vous devriez poster un extrait de code pour nous montrer ce qui se passe réellement.

@Jeff Hubbard ( commentaire ):

  

Cela me fournit en fait par inadvertance la solution que je veux: je peux définir pvData sur NULL sur _HOOK_FREE et ne pas avoir de problèmes avec 0xFEEEFEEE pour mon adresse de pointeur.

Si cela fonctionne pour vous, cela signifie que vous lisez la mémoire libérée lorsque vous testez le pointeur NULL (le pointeur se trouve dans la mémoire que vous avez libérée).

Ceci est un bug.

La "solution" que vous utilisez est simplement de cacher, et non de réparer, le bogue. Lorsque cette mémoire libérée est allouée à autre chose, vous allez soudainement utiliser la mauvaise valeur comme pointeur vers la mauvaise chose.

C’est en fait une fonctionnalité très intéressante dans VC ++ (et je crois d’autres compilateurs) car elle vous permet de voir la mémoire non allouée pour un pointeur dans le débogueur. Je vais y réfléchir à deux fois avant de désactiver cette fonctionnalité. Lorsque vous supprimez un objet en C ++, vous devez définir le pointeur sur NULL au cas où quelque chose tente par la suite de le supprimer. Cette fonctionnalité vous permettra de repérer les endroits où vous avez oublié de placer le pointeur sur <=>.

Si cela fonctionne en mode release, c'est grâce à la chance du cisaillement.

Mike B a raison de supposer que le correctif de débogage cache un bogue. En mode de publication, un pointeur utilisé a été libéré mais non défini sur NULL et la mémoire pointée est toujours & "Valide &"; À un moment donné, les allocations de mémoire changeront ou l'image de la mémoire changera, ou quelque chose causera un & "Valide &"; Le bloc mémoire doit devenir " invalide " ;. À ce stade, votre version de compilation commencera à échouer. Passer en mode débogage pour trouver le problème sera inutile, car le mode débogage a été & Quot; corrigé & Quot;.

Je pense que nous sommes tous d'accord pour dire que le code suivant ne devrait pas fonctionner.

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

Comme presque toutes les autres affiches l'ont dit, les pointeurs devraient être réglés sur delete après avoir appelé <=>. Que vous le fassiez vous-même ou que vous utilisiez boost ou un autre wrapper, ou même que la macro de ce fil dépend de vous.

  

Ce qui se passe, c'est que mon code se bloque   sous une compilation de débogage, mais   réussit sous une compilation de version.

La version de publication plantera sur la machine du client. C'est toujours le cas.

  

Je l'ai vérifié sous un débogueur et   mes pointeurs se préparent à   0xFEEEFEEE après avoir appelé delete on   eux.

Les

pointeurs ne sont pas modifiés après leur appel à la suppression. C'est la mémoire sur laquelle ils pointent qui est définie sur 0xfeeefeee, 0xfeeefeee, ..., 0xfeeefeee.

Si vous remarquez que votre programme lit les données de la mémoire libérée (ce qui est indiqué par le motif 0xfeeefeee dans DEBUG build), vous avez un bogue.

@ [Jeff Hubbard]:

  

Ce qui se passe, c'est que mon code se bloque lors d'une compilation de débogage, mais réussit sous une compilation de publication. Je l'ai vérifié sous un débogueur et mes pointeurs se configurent sur 0xFEEEFEEE après que j'aie appelé delete sur eux. Encore une fois, le même code à la sortie ne plante pas et se comporte comme prévu.

C’est un comportement très étrange - je suis toujours convaincu qu’il existe probablement un bogue caché caché par la _CrtSetAllocHook() solution de contournement.

La <=> signature est utilisée par le gestionnaire de tas du système d'exploitation pour indiquer la mémoire libérée (voir http://www.nobugs.org/developer/win32/debug_crt_heap.html ). Par hasard, pouvez-vous publier du code de repro et indiquer exactement la version du compilateur que vous utilisez?

Je suis à peu près sûr que vous ne pouvez pas désactiver la valeur par défaut de Visual Studio ici, et même si vous le faisiez, la valeur serait alors identique à ce qui était en mémoire avant que la mémoire ne soit allouée.

Votre meilleur a juste l'habitude de les régler à 0 en premier lieu, ce ne sont que 2 personnages supplémentaires.

int *ptr=0;

Vous pouvez également utiliser la macro NULL, définie comme 0 (mais pas par défaut). Soyez donc prudent avec plusieurs définitions lorsque vous incluez des éléments tels que windows.h et que vous les définissez vous-même!

si vous utilisez malloc, cela n’initialise pas la mémoire. vous obtenez n'importe quoi. si vous souhaitez allouer un bloc et l'initialiser à 0, utilisez 'calloc', qui ressemble à malloc uniquement lors de l'initialisation (paramètre de taille d'élément que vous définissez sur 1 si vous souhaitez émuler malloc). vous devriez lire sur calloc avant de l'utiliser car il présente quelques légères différences.

Pourquoi ne pas créer votre propre #define et prendre l’habitude de l’utiliser?

I.e.

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

Évidemment, vous pouvez le nommer comme bon vous semble. deleteZ, deletesafe, tout ce avec quoi vous êtes à l'aise.

Vous pouvez également créer un gestionnaire de mémoire. Ensuite, vous pouvez remplacer new et delete pour extraire / remettre un bloc de mémoire préalloué.

scroll top