Pergunta

Como todos sabem, o Visual C++ o tempo de execução marca blocos de memória não inicializados ou apenas liberados com marcadores especiais diferentes de zero.Existe alguma maneira de desabilitar totalmente esse comportamento sem definir manualmente toda a memória não inicializada como zeros?Isso está causando estragos nas minhas verificações válidas e não nulas, já que 0xFEEEFEEE != 0.

Hmm, talvez eu deva explicar um pouco melhor.Eu crio e inicializo uma variável (via new) e tudo corre bem.Quando eu o libero (via delete), ele define o ponteiro para 0xFEEEFEEE em vez de NULL.Quando insiro um cheque adequado para NULL, como todos os bons programas que gerenciam sua própria memória deveriam, eu encontro problemas como 0xFEEEFEEE passa um NULL verifique sem problemas.Existe alguma boa maneira, além de definir manualmente todos os ponteiros para NULL ao excluí-los, para detectar quando a memória já foi liberada?Eu preferiria não usar Impulsionar simplesmente porque não quero a sobrecarga, por menor que seja, já que é a única coisa para a qual usaria o Boost.

Foi útil?

Solução

Não é responsabilidade de delete para redefinir todos os ponteiros para o objeto para NULL.Além disso, você não deve alterar o preenchimento de memória padrão para o tempo de execução do Windows DEBUG e deve usar algo como boost::shared_ptr<> para dicas de qualquer maneira.

Dito isto, se você realmente quiser dê um tiro no próprio pé você pode.

Você pode mudar o preenchimento padrão para as janelas Tempo de execução de depuração usando um gancho alocador como este.Isso só funcionará em objetos alocados 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;
}

Outras dicas

Ao criar um ponteiro, inicialize-o explicitamente para NULL.Da mesma forma depois de um delete.Dependendo do valor dos dados não inicializados (exceto em alguns casos específicos), há problemas.

Você pode evitar muitas dores de cabeça usando uma classe de ponteiro inteligente (como boost::shared_ptr) que tratará automaticamente se um ponteiro foi inicializado ou não.

O comportamento do VC++ não deve causar estragos em nenhum válido verifique se você pode fazer.Se você está vendo o 0xfeeefeee, então você não gravou na memória (ou a liberou), então você não deveria estar lendo da memória de qualquer maneira.

Se você estiver lendo uma memória não inicializada, suas verificações certamente não serão "válidas".A memória é liberada.Pode já estar em uso para outra coisa.Você não pode fazer suposições sobre o conteúdo da memória não inicializada em C/C++.

Java (e C #, acredito) garantirá que a memória alocada seja zerada antes do uso e, é claro, a coleta de lixo impede que você veja a memória liberada.Mas isso não é uma propriedade do heap C, que expõe a memória diretamente.

Se você construir no modo Release em vez do modo Debug, o tempo de execução não preencherá a memória não inicializada, mas ainda assim não será zero.No entanto, você deve não dependem desse comportamento - você deve inicializar explicitamente a memória com memset(), ZeroMemory() ou SecureZeroMemory() ou definir um sinalizador em algum lugar indicando que a memória ainda não foi inicializada.A leitura da memória não inicializada resultará em comportamento indefinido.

Você diz:

Eu crio e inicializo uma variável (via new) e tudo corre bem.Quando eu o libero (via delete), ele define o ponteiro para 0xFEEEFEEE em vez de NULL.Quando insiro uma verificação adequada para NULL, como deveriam fazer todos os bons programas que gerenciam sua própria memória, tenho problemas, pois 0xFEEEFEEE passa em uma verificação NULL sem problemas.

Mesmo as rotinas de heap de depuração do MSVC não alterarão o valor do ponteiro você está excluindo - o valor do ponteiro que você está excluindo não será alterado (mesmo para NULL).Parece que você está acessando um ponteiro que pertence ao objeto que você acabou de excluir, o que é um bug puro e simples.

Tenho certeza de que o que você está tentando fazer simplesmente encobrirá um acesso inválido à memória.Você deveria postar um trecho de código para nos mostrar o que realmente está acontecendo.

@Jeff Hubbard (Comente):

Na verdade, isso me fornece inadvertidamente a solução que desejo:Posso definir pvData como NULL em _HOOK_FREE e não ter problemas com 0xFEEEFEEE para o endereço do meu ponteiro.

Se isso estiver funcionando para você, significa que você está lendo a memória liberada ao testar o ponteiro NULL (ou seja, o próprio ponteiro reside na memória que você liberou).

Isso é um bug.

A 'solução' que você está usando é simplesmente ocultar, e não corrigir, o bug.Quando essa memória liberada for alocada para outra coisa, de repente você estará usando o valor errado como um ponteiro para a coisa errada.

Na verdade, esse é um recurso muito interessante no VC++ (e acredito em outros compiladores) porque permite que você veja a memória não alocada para um ponteiro no depurador.Vou pensar duas vezes antes de desabilitar essa funcionalidade.Ao excluir um objeto em C++, você deve definir o ponteiro para NULL caso algo mais tarde tente excluir o objeto novamente.Este recurso permitirá que você identifique os locais onde você esqueceu de definir o ponteiro NULL.

Se estiver funcionando no modo de liberação, é por causa da sorte.

Mike B está certo ao presumir que a correção de depuração está escondendo um bug.No modo de liberação, está sendo usado um ponteiro que foi liberado, mas não definido para NULL, e a memória para a qual aponta ainda é "válida".Em algum momento no futuro, as alocações de memória serão alteradas, ou a imagem da memória será alterada, ou algo fará com que o bloco de memória “válido” se torne “inválido”.Nesse ponto, sua versão começará a falhar.Mudar para o modo de depuração para encontrar o problema será inútil, porque o modo de depuração foi "consertado".

Acho que todos concordamos que o código a seguir não deveria funcionar.

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

Como quase todos os outros postadores disseram, os ponteiros devem ser definidos para NULL depois de ligar delete.Se você faz isso sozinho ou usa boost ou algum outro wrapper ou até mesmo a macro neste tópico, depende de você.

O que está acontecendo é que meu código trava sob uma compilação de depuração, mas é bem -sucedido em uma compilação de liberação.

A versão de lançamento irá travar na máquina do cliente.Sempre acontece.

Eu o verifiquei sob um depurador e meus ponteiros estão sendo definidos como 0xfeeefeee depois que eu ligo para excluir neles.

Ponteiros não são alterados depois que você chama delete neles.É a memória para a qual eles apontam que é definida como 0xfeeefeee, 0xfeeefeee, ..., 0xfeeefeee.

Se você perceber que seu programa lê dados da memória liberada (o que é convenientemente indicado pelo padrão 0xfeeefeee na compilação DEBUG), você tem um bug.

@[Jeff Hubbard]:

O que está acontecendo é que meu código falha em uma compilação de depuração, mas é bem-sucedido em uma compilação de lançamento.Eu verifiquei em um depurador e meus ponteiros estão sendo configurados para 0xFEEEFEEE depois de ligar para delete neles.Novamente, o mesmo código no lançamento não trava e se comporta conforme o esperado.

Este é um comportamento muito estranho - ainda estou convencido de que provavelmente há um bug latente que está sendo escondido pelo _CrtSetAllocHook() Gambiarra.

O 0xFEEEFEEE assinatura é usada pelo gerenciador de heap do SO para indicar memória liberada (consulte http://www.nobugs.org/developer/win32/debug_crt_heap.html).Por acaso você pode postar algum código de reprodução e indicar exatamente qual versão do compilador você está usando?

Tenho certeza de que você não pode desativar o padrão do Visual Studio aqui e, mesmo que o fizesse, o valor seria exatamente o que estava na memória antes de a memória ser alocada.

É melhor adquirir o hábito de defini-los como 0 em primeiro lugar, são apenas 2 caracteres extras.

int *ptr=0;

Você também pode usar a macro NULL, que é definida como 0 (mas não é o padrão, então tome cuidado com múltiplas definições ao incluir coisas como windows.h e defini-las você mesmo!

se você estiver usando malloc, ele não inicializa a memória para nada.você consegue o que quer que seja.se você quiser alocar um bloco e inicializá-lo com 0, use 'calloc' que é como malloc apenas com inicialização (um parâmetro de tamanho de elemento que você define como 1 se quiser emular malloc).você deve ler calloc antes de usá-lo, pois ele apresenta algumas pequenas diferenças.

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

Por que não criar o seu próprio #define e adquirir o hábito de usá-lo?

Ou seja

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

Obviamente você pode nomeá-lo como quiser.deleteZ, deletesafe, o que você quiser.

Você também pode criar um gerenciador de memória.Então você pode substituir new e delete para extrair/recuperar um cartucho de memória pré-alocado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top