VC++ 中未初始化的内存块
众所周知,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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
@Jeff Hubbard(评论):
如果这对您有用,则意味着您在测试 NULL 指针时正在读取已释放的内存(即指针本身驻留在您释放的内存中)。
这是一个错误。
您使用的“解决方案”只是隐藏而不是修复错误。 当释放的内存被分配给其他东西时,突然间您将使用错误的值作为指向错误事物的指针。
@Jeff Hubbard (comment):
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.
如果您在发布模式而不是调试模式下构建,运行时根本不会填充未初始化的内存,但它仍然不会为零。 但是,您不应该依赖于这种行为 - 您应该使用 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.
你说:
即使是 MSVC 的调试堆例程也不会更改您要删除的指针的值 - 您要删除的指针的值不会更改(甚至更改为 NULL)。 听起来您正在访问属于您刚刚删除的对象的指针,这是一个错误,简单明了。
我很确定你想要做的只是掩盖无效的内存访问。 您应该发布一段代码来向我们展示到底发生了什么。
You say:
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.
如果您正在读取未初始化的内存,那么您的检查肯定不是“有效的”。 内存被释放。 它可能已经用于其他用途。 您不能对 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.
delete
没有责任将所有指向该对象的指针重置为NULL
。另外,您不应该更改 Windows DEBUG 运行时的默认内存填充,并且应该以任何方式使用诸如
boost::shared_ptr
之类的东西作为指针。也就是说,如果你真的想搬起石头砸自己的脚,你也可以。
您可以通过使用这样的分配器挂钩来更改 Windows DEBUG 运行时的默认填充。 这仅适用于堆分配的对象!
It is not the responsibility of
delete
to reset all the pointers to the object toNULL
.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!
创建指针时,将其显式初始化为
NULL
。 在删除
之后也是如此。 依赖于未初始化数据的值(少数特定情况除外)是自找麻烦。通过使用智能指针类(例如
boost::shared_ptr
)它将自动处理指针是否已初始化。When you create a pointer, explicity initialize it to
NULL
. Likewise after adelete
. 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.
我很确定你不能在这里禁用 Visual Studio 默认值,即使你这样做了,该值也将是分配内存之前内存中的值。
你最好一开始就养成将它们设置为 0 的习惯,这只是 2 个额外的字符。
您还可以使用 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.
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!
@[杰夫·哈伯德]:
这是非常奇怪的行为 - 我仍然相信
_CrtSetAllocHook()
解决方法可能隐藏了一个潜在的错误。操作系统堆管理器使用
0xFEEEFEEE
签名来指示已释放的内存(请参阅 http://www.nobugs.org/developer/win32/debug_crt_heap.html)。 您是否可以发布一些重现代码并准确指出您正在使用哪个编译器版本?@[Jeff Hubbard]:
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?这实际上是 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 toNULL
.发布版本将在客户的机器上崩溃。 总是如此。
指针在调用delete后不会改变。 它们指向的内存被设置为 0xfeeefeee、0xfeeefeee、...、0xfeeefeee。
如果您发现您的程序从释放的内存中读取数据(在 DEBUG 构建中通过 0xfeeefeee 模式方便地指示),则说明您遇到了错误。
Release build will crash on customer's machine. It always does.
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.
如果它在发布模式下工作,那是因为运气不好。
Mike B 认为调试修复隐藏了错误,这是正确的。 在释放模式下,正在使用一个已被释放但未设置为 NULL 的指针,并且它指向的内存仍然“有效”。 在未来的某个时刻,内存分配会发生变化,或者内存映像会发生变化,或者某些事情会导致“有效”内存块变得“无效”。 那时,您的发布版本将开始失败。 切换到调试模式来查找问题是没有用的,因为调试模式已经被“修复”了。
我想我们都同意下面的代码不应该工作。
正如几乎所有其他发帖者所说,在调用
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.
As just about every other poster has said, pointers should be set to
NULL
after callingdelete
. Whether you do it yourself or use boost or some other wrapper or even the macro in this thread is up to you.如果您使用 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
为什么不创建自己的#define 并养成使用它的习惯呢?
即
显然你可以将其命名为你喜欢的任何名称。 deleteZ、deletesafe,无论你喜欢什么。
Why not create your own #define and get in the habit of using it?
I.e.
Obviously you can name it whatever you like. deleteZ, deletesafe, whatever you're comfortable with.
您也可以创建一个内存管理器。 然后,您可以覆盖 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.