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

发布于 2024-07-05 08:14:56 字数 664 浏览 7 评论 0原文

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

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

As everyone knows, the Visual C++ runtime marks uninitialized or just freed memory blocks with special non-zero markers. Is there any way to disable this behavior entirely without manually setting all uninitialized memory to zeros? It's causing havoc with my valid not null checks, since 0xFEEEFEEE != 0.

Hrm, perhaps I should explain a bit better. 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. Is there any good way, other than manually setting all pointers to NULL when deleting them, to detect when memory has already been freed? I would prefer to not use Boost simply because I don't want the overhead, small though it may be, since that's the only thing I'd be using Boost for.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

゛时过境迁 2024-07-12 08:14:56

@Jeff Hubbard(评论):

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

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

这是一个错误。

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

@Jeff Hubbard (comment):

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.

If this is working for you, then it means that you are reading freed memory when you're testing for the NULL pointer (ie., the pointer itself resides in the memory you freed).

This is a bug.

The 'solution' you're using is simply hiding, not fixing, the bug. When that freed memory ever gets allocated to something else, suddenly you'll be using the wrong value as a pointer to the wrong thing.

谜兔 2024-07-12 08:14:56

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

If you build in Release mode instead of Debug mode, the runtime does not fill uninitialized memory at all, but it will still not be zeros. However, you should not depend on this behavior - you should either explicitly initialize the memory yourself with memset(), ZeroMemory(), or SecureZeroMemory(), or set a flag somewhere indicating that the memory is not yet initialized. Reading uninitialized memory will result in undefined behavior.

慕巷 2024-07-12 08:14:56

你说:

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

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

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

You say:

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.

Even the debug heap routines of MSVC will not change the value of the pointer you're deleting - the value of the pointer you're deleting will not change (even to NULL). It sounds like you're accessing a pointer that belongs to the object you've just deleted, which is a bug, plain and simple.

I'm pretty sure that what you're trying to do will simply cover up an invalid memory access. You should post a snippet of code to show us what is really happening.

雄赳赳气昂昂 2024-07-12 08:14:56

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

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

If you're reading uninitialized memory, your checks are most certainly not "valid". The memory is freed. It might already be in use for something else. You can't make any assumptions about the contents of uninitialized memory in C/C++.

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. But that isn't a property of the C heap, which exposes the memory directly.

并安 2024-07-12 08:14:56

delete 没有责任将所有指向该对象的指针重置为 NULL
另外,您不应该更改 Windows DEBUG 运行时的默认内存填充,并且应该以任何方式使用诸如 boost::shared_ptr 之类的东西作为指针。

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

您可以通过使用这样的分配器挂钩来更改 Windows DEBUG 运行时默认填充。 这仅适用于堆分配的对象!

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

It is not the responsibility of delete to reset all the pointers to the object to NULL.
Also you shouldn't change the default memory fill for the windows DEBUG runtime and you should use some thing like boost::shared_ptr<> for pointers any way.

That said, if you really want to shoot your self in the foot you can.

You can change the default fill for the windows DEBUG runtime by using an allocator hook like this. This will only work on HEAP allocated object!

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;
}
匿名。 2024-07-12 08:14:56

创建指针时,将其显式初始化为NULL。 在删除之后也是如此。 依赖于未初始化数据的值(少数特定情况除外)是自找麻烦。

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

When you create a pointer, explicity initialize it to NULL. Likewise after a delete. Depending on the value of uninitialized data (except in a few specific cases) is asking for trouble.

You can save yourself a lot of headaches by using a smart pointer class (such as boost::shared_ptr) which will automatically deal with whether a pointer is initialized or not.

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

VC++'s behaviour shouldn't cause havoc with any valid check you can do. If you are seeing the 0xfeeefeee then you haven't written to the memory (or have freed it), so you shouldn't be reading from the memory anyway.

你不是我要的菜∠ 2024-07-12 08:14:56

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

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

int *ptr=0;

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

I'm pretty sure you can't disable the visual studio default here, and even if you did, the value would then be just whatever was in memory before the memory was allocated.

Your best off just getting in the habit of setting them to 0 in the first place, it's only 2 extra charecters.

int *ptr=0;

You can also use the NULL macro, which is defined as 0 (but not be default, so be carful with multiple definitions when includeing stuff like windows.h and defining it yourself!

清浅ˋ旧时光 2024-07-12 08:14:56

@[杰夫·哈伯德]:

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

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

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

@[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.

This is very strange behavior - I'm still convinced that there's probably a latent bug that's being hidden by the _CrtSetAllocHook() workaround.

The 0xFEEEFEEE signature is used by the OS heap manager to indicate freed memory (see http://www.nobugs.org/developer/win32/debug_crt_heap.html). By any chance can you post some repro code and indicate exactly which compiler version you're using?

枫以 2024-07-12 08:14:56

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

That is actually a very nice feature in VC++ (and I believe other compilers) because it allows you to see unallocated memory for a pointer in the debugger. I will think twice before disabling that functionality. When you delete an object in C++ you should set the pointer to NULL in case something later tries to delete the object again. This feature will allow you to spot the places where you forgot to set the pointer to NULL.

滿滿的愛 2024-07-12 08:14:56

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

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

我已经在调试器下检查过它并且
我的指针正在设置为
0xFEEEFEEE 在我调用删除后
他们。

指针在调用delete后不会改变。 它们指向的内存被设置为 0xfeeefeee、0xfeeefeee、...、0xfeeefeee。

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

What's happening is my code crashes
under a debug compilation, but
succeeds under a release compilation.

Release build will crash on customer's machine. It always does.

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

Pointers are not changed after you call delete on them. It's the memory they point to that gets set to 0xfeeefeee, 0xfeeefeee, ..., 0xfeeefeee.

If you spot that your program reads data from freed memory (which is conveniently indicated by 0xfeeefeee pattern in DEBUG build), you have a bug.

眼波传意 2024-07-12 08:14:56

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

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

正如几乎所有其他发帖者所说,在调用 delete 后,指针应设置为 NULL。 无论您自己执行还是使用 boost 或其他包装器,甚至此线程中的宏都取决于您。

If it's working in release mode, it's because of shear luck.

Mike B is right to assume that the debug fix is hiding a bug. In release mode, a pointer is being used that has been freed but not set to NULL, and the memory it points to is still "valid". At some point in the future, memory allocations will change, or the memory image will change, or something will cause the "valid" memory block to become "invalid". At that point, your release build will start failing. Switching to debug mode to find the problem will be useless, because the debug mode has been "fixed".

I think we call all agree that the following code shouldn't work.

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

As just about every other poster has said, pointers should be set to NULL after calling delete. Whether you do it yourself or use boost or some other wrapper or even the macro in this thread is up to you.

寂寞笑我太脆弱 2024-07-12 08:14:56

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

http://wiki.answers.com/Q/What_is_the_difference_ Between_malloc_and_calloc_functions

if you are using malloc, it does not intialize the memory to anything. you get whatever. if you want to allocate a block and initialize it to 0, use 'calloc' which is like malloc only with initialization (an an element size parameter which you set to 1 if you want to emulate malloc). you should read up on calloc before using it as it has some slight differences.

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

没有你我更好 2024-07-12 08:14:56

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

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

显然你可以将其命名为你喜欢的任何名称。 deleteZ、deletesafe,无论你喜欢什么。

Why not create your own #define and get in the habit of using it?

I.e.

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

Obviously you can name it whatever you like. deleteZ, deletesafe, whatever you're comfortable with.

奈何桥上唱咆哮 2024-07-12 08:14:56

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

You could create a memory manager also. Then you could override new and delete to pull from/put back a pre allocated chuck of memory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文