繁体   English   中英

VC++ 中未初始化的 memory 块

[英]Uninitialized memory blocks in VC++

众所周知, Visual C++运行时用特殊的非零标记标记未初始化或刚刚释放的 memory 块。 有没有办法完全禁用此行为而无需手动将所有未初始化的 memory 设置为零? 它对我的有效非 null 检查造成严重破坏,因为0xFEEEFEEE != 0

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

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

通过使用智能指针 class (例如boost::shared_ptr ),您可以省去很多麻烦,它会自动处理指针是否被初始化。

VC++ 的行为不应该对您可以执行的任何有效检查造成严重破坏。 如果您看到 0xfeeefeee,那么您还没有写入 memory(或已释放它),所以无论如何您都不应该从 memory 中读取。

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

Java (and C#, I believe) will guaranteed that allocated memory is zeroed before use, and of course the garbage collection prevents you from seeing freed memory at all. 但这不是 C 堆的属性,它直接暴露了 memory。

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

将所有指向 object 的指针重置为NULL不是delete的责任。 此外,您不应更改 windows DEBUG 运行时的默认 memory 填充,您应该以任何方式使用诸如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;
}

你说:

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

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

我很确定您要做的只是掩盖无效的 memory 访问。 您应该发布一段代码,向我们展示真正发生的事情。

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

@杰夫哈伯德( 评论):

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

如果这对您有用,则意味着您在测试 NULL 指针时正在读取已释放的 memory(即,指针本身位于 ZCD69B4957F06CD818D7BF3D61980E2 中)

这是一个错误。

您使用的“解决方案”只是隐藏而不是修复错误。 当释放的 memory 被分配给其他东西时,突然间你会使用错误的值作为指向错误东西的指针。

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

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

我想我们都同意以下代码不应该工作。

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

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

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

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

我已经在调试器下检查了它,并且在我对它们调用 delete 后,我的指针被设置为 0xFEEEFEEE。

调用 delete 后,指针不会更改。 他们指向的 memory 被设置为 0xfeeefeee、0xfeeefeee、...、0xfeeefeee。

如果你发现你的程序从释放的 memory 读取数据(在 DEBUG 构建中通过 0xfeeefeee 模式方便地指示),那么你就有一个错误。

@[杰夫哈伯德]:

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

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

操作系统堆管理器使用0xFEEEFEEE签名来指示已释放的 memory(请参阅http://www.nobugs.org/developer/win32/debug_crt_heap.ZFC35FZ8830D5FCC69D539E 您是否可以发布一些重现代码并准确指出您正在使用的编译器版本?

您也可以创建一个 memory 管理器。 然后您可以覆盖 new 和 delete 以拉出/放回 memory 的预分配卡盘。

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

最好的办法是养成一开始就将它们设置为 0 的习惯,这只是额外的 2 个字符。

int *ptr=0;

您还可以使用定义为 0 的 NULL 宏(但不是默认值,因此在包含 windows.h 之类的内容并自行定义时,请注意多个定义!

如果您使用的是 malloc,它不会将 memory 初始化为任何东西。 你得到什么。 如果您想分配一个块并将其初始化为 0,请使用类似于 malloc 的“calloc”,仅进行初始化(如果您想模拟 malloc,您将其设置为 1 的元素大小参数)。 你应该在使用 calloc 之前阅读它,因为它有一些细微的差别。

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

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

IE

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

显然,您可以随意命名它。 deleteZ,deletesafe,随便你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM