是否可以将一段内存标记为“越界”?所以堆管理器不会从中分配?

发布于 2024-10-04 11:59:55 字数 1795 浏览 8 评论 0原文

今天早些时候,我问了这个问题

在花了一些时间调查这个问题后,我发现了发生了什么。我将其作为一个新问题发布,因为我认为它足够有趣,可以作为一个单独的问题进行跟踪。我将用答案(以及指向此问题的链接)更新该问题。

从调试器启动单元测试

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194     /* Different memory location */

从命令行启动单元测试

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960     /* Same memory location */

总之:

  • 命令行启动单元测试时,后续调用 new 分配一个对象(在分配新对象之前删除之前的对象)总是返回相同的 内存中的地址。
  • 调试器启动单元测试时,后续调用new来分配对象删除之前的分配新对象之前的对象)始终返回内存中的唯一地址。

问题是,因为通过命令行启动时对象的分配总是在内存中获得相同的地址,所以我正在访问的存储了旧指针的映射仍然可以使用并且测试不会崩溃。但我希望我的单元测试在缺陷修复未到位时崩溃,以确保它不会默默地失败并且缺陷不会再次出现。

我的问题有两个部分:

  1. 为什么堆管理器在从命令行启动单元测试时会重用同一部分内存,但当我从调试器启动单元测试时却不会?

  2. 是否有我可以在测试工具上使用的编译器设置,或者我可以调用的方法来防止堆管理器重新使用我已删除的内存部分,以允许我正确编写单元测试? 1


1Obviously one way of doing this is to not delete the original object, but the part of the code that allocates this is in my production code, and I doing this would result in memory leaks.

Earlier today I asked this question.

After spending some time investigating this issue, I have discovered what is going on. I am posting this as a new question because I think it is interesting enough to track as a separate issue. I will update that question with the answer (and a link to this one).

Launching unit test from debugger

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194     /* Different memory location */

Launching unit test from command line

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960     /* Same memory location */

In summary:

  • When launching the unit test from the command line, subsequent calls to new to allocate an Object (deleteing the previous Object before allocating a new one) always return the same address in memory.
  • When launching the unit test from the debugger, subsequent calls to new to allocate an Object (deleteing the previous Object before allocating a new one) always return a unique address in memory.

The problem is that because allocations of Object always get the same address in memory when launching through the command line, a map which I am accessing which has stored the old pointer can still be used and the test won't crash. But I want my unit test to crash when the defect fix is not in place, to ensure that it doesn't silently fail and the defect doesn't come back.

There are 2 parts to my question:

  1. Why would the heap manager re-use the same part of memory when launching a unit test from the command line, but not when I launch the unit test from the debugger?

  2. Is there a compiler setting I could use on my test harness, or a method I can call to prevent the heap manager from re-using a section of memory that I have deleted, to allow me to correctly write my unit test? 1


1Obviously one way of doing this is to not delete the original object, but the part of the code that allocates this is in my production code, and I doing this would result in memory leaks.

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

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

发布评论

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

评论(4

一抹微笑 2024-10-11 11:59:55

您的单元测试有缺陷,因为它依赖于未定义的行为。您应该重写单元测试,以便它不依赖于未定义的行为,在这种情况下,无论内存管理器决定如何分配内存,它总是会通过。

您正在做的是这样的:

Object* pObject = new Object(...);
...
delete pObject;
pObject = new Object(...);
// Use dangling pointer to first object, and if it crashes, the unit test fails
// This is WRONG since a crash isn't guaranteed

您应该重新构建单元测试,使其工作方式如下:

Object* pObject = new Object(...);
...
// Check to see if there are dangling references to pObject right before we
// delete it.  If there are, assert() and fail the unit test.
assert(NoDanglingReferences(pObject));
delete pObject;
// Continue on with more tests

Your unit test is flawed, since it's relying on undefined behavior. You should rewrite your unit test so that it doesn't rely on undefined behavior, in which case it will always pass regardless of how the memory manager decides to allocate memory.

What you're doing is this:

Object* pObject = new Object(...);
...
delete pObject;
pObject = new Object(...);
// Use dangling pointer to first object, and if it crashes, the unit test fails
// This is WRONG since a crash isn't guaranteed

You should instead restructure the unit test so it works like this:

Object* pObject = new Object(...);
...
// Check to see if there are dangling references to pObject right before we
// delete it.  If there are, assert() and fail the unit test.
assert(NoDanglingReferences(pObject));
delete pObject;
// Continue on with more tests
瞎闹 2024-10-11 11:59:55

您可以将 newdelete 替换为具有所需行为的您自己的版本。

You could replace new and delete with your own versions that have the behaviour you want.

月朦胧 2024-10-11 11:59:55

首先 - 不在“正常”内存管理器中。一旦释放内存,您就将其所有权传递给内存管理器,后者可以重用它。

您可以 按照用户 Andreas Brinck 建议编写自定义管理器,但是什么会吗?它不会从空气中制造内存,而是从 CRT 堆或操作系统堆等地方请求内存。

场景 A。它不会将内存返回到底层堆 - 将会发生泄漏,并且内存块仍将被映射到地址空间并且可以访问。

场景 B。它会将内存返回到底层堆 - 然后当您的管理员尝试再次分配内存时,底层堆可以再次返回该块。此外,当您将内存返回给底层堆时,您也不知道底层堆会做什么。它可能会使其未映射或未映射 - 因此访问该内存可能会崩溃或不会。

底线是你完蛋了。尝试测试未定义的行为不会很有成效。

First of all - not in a "normal" memory manager. Once you deallocate memory you pass ownership of it to the memory manager and the latter can reuse it.

You could write a custom manager as user Andreas Brinck suggests, but what would it do? It doesn't craft memory from air, it requests it from somewhere like CRT heap or operating system heap.

Scenario A. It wouldn't return memory to the underlying heap - you'll have a leak and the memory block will still be mapped into the address space and it will be accessible.

Scenario B. It will return memory to underlying heap - then when your mananger tries to allocate memory again the underlying heap can return that block again. Also you don't know what the underlying heap does when you return memory to it. It might make it unmapped or not - so accessing that memory might crash or not.

The bottom line is you're screwed. Trying to test undefined behavior is not going to be very productive.

半世蒼涼 2024-10-11 11:59:55

这是未定义行为的示例。 C++ 或堆管理器都没有定义如何分配内存。您不能依赖内存被重用或不被重用。当您执行上述操作时,无法确定或更改返回的指针是否与第一个分配的指针不同。

This is an example of UNDEFINED BEHAVIOUR. Neither C++ or the heap manager define how memory is going to be allocated. You cannot rely on either memory being reused or not being reused. When you do something like in the above, there is no way to determine or change whether or not the pointer returned will be different from the first allocated.

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