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

时间:2020-03-05 18:54:15  来源:igfitidea点击:

众所周知,Visual C ++运行时使用特殊的非零标记来标记未初始化或者刚刚释放的内存块。有什么方法可以完全禁用此行为,而无需将所有未初始化的内存手动设置为零?由于0xFEEEFEEE!= 0,我的有效非null检查造成了严重破坏。

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

解决方案

回答

创建指针时,将其显式初始化为NULL。同样在"删除"之后。取决于未初始化数据的值(在某些特定情况下除外)会带来麻烦。

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

回答

我很确定我们不能在此处禁用Visual Studio的默认值,即使我们这样做了,该值也将恰好是分配内存之前内存中的值。

最好只是习惯于将它们设置为0的习惯,因为它只有2个额外的字符。

int *ptr=0;

我们还可以使用NULL宏,该宏被定义为0(但不是默认值),因此在包含诸如windows.h之类的内容并自己定义时要小心多个定义!

回答

VC ++的行为不会对我们可以执行的任何有效检查造成破坏。如果我们看到0xfeeefeee,则说明我们尚未写入内存(或者已将其释放),因此无论如何我们都不应该从内存中读取内容。

回答

如果以发布模式而不是调试模式构建,则运行时根本不会填充未初始化的内存,但仍不会为零。但是,我们不应该依赖于此行为,我们应该使用memset(),ZeroMemory()或者SecureZeroMemory()显式地初始化内存,或者在某个位置设置标志以指示内存尚未初始化。读取未初始化的内存将导致未定义的行为。

回答

如果我们正在读取未初始化的内存,则检查肯定是"无效的"。内存已释放。它可能已经用于其他用途。我们不能对C / C ++中未初始化内存的内容做任何假设。

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

回答

将所有指向该对象的指针重置为" 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;
}

回答

如果使用的是malloc,则不会将内存初始化为任何内容。你得到什么。如果要分配一个块并将其初始化为0,请使用" calloc",这与仅在初始化时使用malloc相似(如果要模拟malloc,则将元素大小参数设置为1)。我们应该在使用calloc之前先阅读一下,因为它略有不同。

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

回答

实际上,这是VC ++(我相信其他编译器)中的一个非常不错的功能,因为它允许我们查看调试器中指针的未分配内存。在禁用该功能之前,我会三思而后行。当我们在C ++中删除对象时,应将指针设置为" NULL",以防以后某些情况再次尝试删除该对象。这个功能将使我们发现忘记将指针设置为NULL的地方。

回答

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

IE。

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

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

回答

你说:

I create and initialize a variable (via new), and that all goes just fine. When I free it (via delete), it sets the pointer to 0xFEEEFEEE instead of NULL. When I insert a proper check for NULL, as all good programs that manage their own memory should, I come up with problems as 0xFEEEFEEE passes a NULL check without problems.

即使MSVC的调试堆例程也不会更改我们要删除的指针的值,也不会更改我们要删除的指针的值(甚至为NULL)。听起来我们正在访问属于刚删除的对象的指针,这是一个简单而又简单的错误。

我很确定我们要执行的操作只会掩盖无效的内存访问。我们应该发布一段代码以向我们展示实际情况。

回答

@Jeff Hubbard(评论):

This actually inadvertently provides me with the solution I want: I can set pvData to NULL on _HOOK_FREE and not run into problems with 0xFEEEFEEE for my pointer address.

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

这是一个错误。

我们正在使用的"解决方案"只是隐藏而不是修复错误。当释放的内存分配给其他对象时,突然我们将使用错误的值作为错误对象的指针。

回答

@ [Jeff Hubbard]:

What's happening is my code crashes under a debug compilation, but succeeds under a release compilation. I've checked it under a debugger and my pointers are getting set to 0xFEEEFEEE after I call delete on them. Again, same code on release doesn't crash and behaves as expected.

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

OS堆管理器使用" 0xFEEEFEEE"签名来指示已释放的内存(请参见http://www.nobugs.org/developer/win32/debug_crt_heap.html)。我们是否有机会发布一些repro代码,并确切说明我们使用的是哪个编译器版本?

回答

如果在释放模式下工作,那是因为运气不好。

Mike B认为调试修复程序隐藏了错误是正确的。在释放模式下,正在使用已释放但未设置为NULL的指针,并且它指向的内存仍为"有效"。在将来的某个时候,内存分配将更改,或者内存映像将更改,或者某种原因将导致"有效"内存块变为"无效"。届时,发布版本将开始失败。切换到调试模式以查找问题将是无用的,因为调试模式已被"修复"。

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

char * p = new char[16];     // 16 bytes of random trash
strcpy(p, "StackOverflow");  // 13 characters, a '
What's happening is my code crashes
  under a debug compilation, but
  succeeds under a release compilation.
' 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

正如几乎所有其他发布者所说的那样,在调用delete之后,应该将指针设置为NULL。无论我们自己执行此操作,还是使用boost或者其他包装器,甚至此线程中的宏都由我们决定。

回答

I've checked it under a debugger and
  my pointers are getting set to
  0xFEEEFEEE after I call delete on
  them.

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

##代码##

调用删除指针后,指针不会更改。他们指向的内存设置为0xfeeefeee,0xfeeefeee,...,0xfeeefeee。

如果发现程序从已释放的内存中读取数据(在DEBUG构建中由0xfeeefeee模式方便地指示),则说明存在错误。