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

嗯,也许我应该解释得更好一些。我创建并初始化一个变量(通过 new),一切都很顺利。当我释放它时(通过删除),它将指针设置为 0xFEEEFEEE 代替 NULL. 。当我插入正确的检查时 NULL, ,正如所有管理自己内存的好程序一样,我提出了以下问题 0xFEEEFEEE 通过一个 NULL 检查没有问题。除了手动将所有指针设置为 NULL 删除它们时,检测内存何时已被释放?我宁愿不使用 促进 只是因为我不想要开销,尽管开销可能很小,因为这是我使用 Boost 的唯一目的。

有帮助吗?

解决方案

这不是以下人员的责任 delete 将所有指向该对象的指针重置为 NULL。另外,您不应该更改 Windows DEBUG 运行时的默认内存填充,您应该使用类似的东西 boost::shared_ptr<> 以任何方式获取指针。

也就是说,如果你真的想要 搬起石头砸自己的脚 你可以。

你可以 改变默认填充 用于窗户 调试运行时 通过使用这样的分配器钩子。这仅适用于堆分配的对象!

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;
}

其他提示

创建指针时,将其显式初始化为 NULL. 。同样地,经过一个 delete. 。依赖于未初始化数据的值(少数特定情况除外)是自找麻烦。

通过使用智能指针类(例如 boost::shared_ptr)它将自动处理指针是否已初始化。

VC++ 的行为不应该对任何 有效的 检查你能做什么。如果您看到 0xfeeefeee,那么您还没有写入内存(或已释放它),因此无论如何您都不应该从内存中读取。

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

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

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

你说:

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

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

我很确定您要做的只是掩盖无效的内存访问。您应该发布一段代码来向我们展示到底发生了什么。

@杰夫·哈伯德(评论):

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

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

这是一个错误。

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

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

如果它在发布模式下工作,那是因为运气不好。

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 或其他包装器,甚至此线程中的宏都取决于您。

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

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

我已经在调试器下检查了它,在调用删除后,我的指针将设置为0xfeeefeee。

指针 在您对它们调用删除后不会更改。它们指向的内存被设置为 0xfeeefeee、0xfeeefeee、...、0xfeeefeee。

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

@[杰夫·哈伯德]:

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

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

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

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

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

int *ptr=0;

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

如果您使用 malloc,它不会将内存初始化为任何内容。你得到任何东西。如果你想分配一个块并将其初始化为 0,请使用“calloc”,它类似于仅带有初始化的 malloc(一个元素大小参数,如果你想模拟 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,无论你喜欢什么。

您也可以创建一个内存管理器。然后,您可以覆盖 new 并删除以从预先分配的内存块中拉出/放回。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top