VC ++中未初始化的内存块

时间:2008-09-15 19:02:36

标签: c++ memory allocation

众所周知,Visual C++运行时标记未初始化或仅释放具有特殊非零标记的内存块。有没有办法完全禁用此行为,而无需手动将所有未初始化的内存设置为零?由于0xFEEEFEEE != 0

,它对我的​​有效非空检查造成了严重破坏

嗯,也许我应该更好地解释一下。我创建并初始化一个变量(通过new),一切都很好。当我释放它(通过删除)时,它将指针设置为0xFEEEFEEE而不是NULL。当我为NULL插入适当的检查时,由于所有管理自己内存的好程序都应该,我会遇到问题,因为0xFEEEFEEE通过NULL检查没有问题。有没有什么好的方法,除了在删除它们时手动设置所有指针NULL,以检测何时已释放内存?我宁愿不使用Boost只是因为我不想要开销,尽管可能很小,因为这是我唯一使用Boost的东西。

15 个答案:

答案 0 :(得分:18)

创建指针时,explicity将其初始化为NULL。同样在delete之后。取决于未初始化数据的值(除少数特定情况外)是在寻找麻烦。

使用智能指针类(例如boost::shared_ptr)可以自动处理指针是否初始化,从而为自己省去了很多麻烦。

答案 1 :(得分:14)

VC ++的行为不应该导致你可以做的任何有效检查造成严重破坏。如果你看到0xfeeefeee然后你没有写入内存(或已经释放它),所以你不应该从内存中读取。

答案 2 :(得分:8)

如果您正在阅读未初始化的内存,那么您的检查肯定不是“有效”。内存被释放。它可能已经被用于其他东西。你不能对C / C ++中未初始化的内存的内容做任何假设。

Java(和C#,我相信)将保证在使用之前已分配的内存为零,当然垃圾收集会阻止您看到释放的内存。但这不是C堆的属性,它直接暴露内存。

答案 3 :(得分:6)

如果您在Release模式而不是Debug模式下构建,则运行时根本不会填充未初始化的内存,但它仍然不会为零。但是,您应该依赖于此行为 - 您应该使用memset(),ZeroMemory()或SecureZeroMemory()自己显式初始化内存,或者在某处设置一个标志,指示内存不是尚未初始化。读取未初始化的内存将导致未定义的行为。

答案 4 :(得分:6)

delete的责任不是将对象的所有指针重置为NULL。 此外,您不应该更改Windows DEBUG运行时的默认内存填充,您应该使用boost::shared_ptr<>之类的东西作为指针。

那就是说,如果你真的想用脚射击自己,你可以。

您可以使用像这样的分配器挂钩更改 Windows DEBUG运行时默认填充。这只适用于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;
}

答案 5 :(得分:5)

你说:

  

我创建并初始化一个变量(通过new),一切都很好。当我释放它(通过删除)时,它将指针设置为0xFEEEFEEE而不是NULL。当我插入一个正确的NULL检查,因为所有管理自己内存的好程序都应该,我会遇到问题,因为0xFEEEFEEE会毫无问题地通过NULL检查。

即使MSVC的调试堆例程也不会更改您正在删除的指针的值 - 您要删除的指针的值不会更改(甚至为NULL)。听起来你正在访问一个属于你刚刚删除的对象的指针,这是一个简单明了的bug。

我很确定您尝试做的事情只会掩盖无效的内存访问。您应该发布一段代码来向我们展示实际发生的事情。

答案 6 :(得分:4)

这实际上是VC ++中的一个非常好的功能(我相信其他编译器),因为它允许您在调试器中查看指针的未分配内存。在禁用该功能之前,我会三思而后行。当您在C ++中删除对象时,您应该将指针设置为NULL,以防稍后尝试再次删除该对象。此功能可让您找到忘记将指针设置为NULL的位置。

答案 7 :(得分:4)

@Jeff Hubbard(comment):

  

这实际上无意中为我提供了我想要的解决方案:我可以在_HOOK_FREE上将pvData设置为NULL,而不会遇到0xFEEEFEEE的指针地址问题。

如果这对您有用,则表示您在测试NULL指针时正在读取释放的内存(即,指针本身位于您释放的内存中)。

这是一个错误。

你正在使用的'解决方案'只是隐藏而不是修复错误。当释放的内存被分配给其他内容时,突然间你将使用错误的值作为指向错误内容的指针。

答案 8 :(得分:4)

如果它在释放模式下工作,那是因为剪切运气。

Mike B认为调试修复程序隐藏了一个错误是正确的。在释放模式下,正在使用已释放但未设置为NULL的指针,并且它指向的内存仍然是“有效”。在将来的某个时刻,内存分配将发生变化,或者内存映像将发生变化,或者某些内容将导致“有效”内存块变为“无效”。此时,您的发布版本将开始失败。切换到调试模式以找到问题将是无用的,因为调试模式已经“修复”。

我认为我们都同意以下代码不起作用。

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

正如几乎所有其他海报所说的那样,在调用NULL后,指针应设置为delete。无论你是自己动手还是使用boost或其他包装器,甚至是这个线程中的宏都取决于你。

答案 9 :(得分:3)

  

发生的事情是我的代码崩溃了   在调试编译下,但是   在发布汇编下成功。

发布版本将在客户的计算机上崩溃。它总是这样。

  

我在调试器下检查过它   我的指针正在设定   我打电话给删除后0xFEEEFEEE   它们。

在您调用删除后,

指针不会更改。这是他们指向的内存设置为0xfeeefeee,0xfeeefeee,...,0xfeeefeee。

如果发现你的程序从释放的内存中读取数据(在DEBUG版本中由0xfeeefeee模式方便地指示),那么你就有错误。

答案 10 :(得分:1)

@ [Jeff Hubbard]:

  

正在发生的事情是我的代码在调试编译下崩溃,但在发布编译下成功。我在调试器下检查了它,并且在我调用delete之后我的指针被设置为0xFEEEFEEE。同样,发布时的相同代码不会崩溃并且行为与预期一致。

这是一种非常奇怪的行为 - 我仍然确信_CrtSetAllocHook()解决方法可能隐藏了潜在的错误。

操作系统堆管理器使用0xFEEEFEEE签名来指示释放的内存(请参阅http://www.nobugs.org/developer/win32/debug_crt_heap.html)。你有没有机会发布一些repro代码并准确指出你正在使用哪个编译器版本?

答案 11 :(得分:0)

我很确定你不能在这里禁用visual studio默认设置,即使你这样做了,那么该值也就是内存分配之前的内存。

你最好养成将它们设置为0的习惯,它只有2个额外的字符。

int *ptr=0;

你也可以使用NULL宏,它被定义为0(但不是默认的,因此当包含像windows.h这样的东西并自己定义它时,请使用多个定义进行处理!

答案 12 :(得分:0)

如果您使用的是malloc,它不会将内存初始化为任何内容。你得到了什么。如果你想分配一个块并将其初始化为0,那么使用'calloc'就像malloc一样只有初始化(如果要模拟malloc,则设置为1的元素大小参数)。你应该在使用之前阅读calloc,因为它有一些细微的差别。

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

答案 13 :(得分:0)

为什么不创建自己的#define并养成使用它的习惯?

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

显然你可以随意命名。 deleteZ,deletesafe,无论你喜欢什么。

答案 14 :(得分:0)

您也可以创建一个内存管理器。然后你可以覆盖new和delete来拉出/放回预先分配的内存。