代码之家  ›  专栏  ›  技术社区  ›  LeopardSkinPillBoxHat

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

  •  1
  • LeopardSkinPillBoxHat  · 技术社区  · 14 年前

    今天早些时候我问 this question .

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

    从调试器启动单元测试

    // 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 分配 Object ( delete 上一个 对象 在分配新的之前)总是返回 相同的 内存中的地址。
    • 从启动单元测试时 调试器 ,后续调用 新的 分配 对象 ( 删除 上一个 对象 在分配新的之前)总是返回 独特的 内存中的地址。

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

    我的问题分为两部分:

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

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


    显然,这样做的一种方法是不删除原始对象,但分配该对象的代码部分在我的生产代码中,这样做会导致内存泄漏。
    4 回复  |  直到 8 年前
        1
  •  2
  •   Adam Rosenfield    14 年前

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

    你要做的是:

    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
    
        2
  •  2
  •   Andreas Brinck    14 年前

    你可以替换 new delete 你自己的版本有你想要的行为。

        3
  •  1
  •   Community CDub    8 年前

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

    你可以 write a custom manager 作为 user Andreas Brinck 建议,但它能做什么呢?它不是从air创建内存,而是从CRT堆或操作系统堆之类的地方请求内存。

    场景A。它不会将内存返回到底层堆-您将有一个漏洞,内存块仍将映射到地址空间,并且可以访问。

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

    归根结底是你搞砸了。尝试测试未定义的行为不会很有成效。

        4
  •  0
  •   Alexander Rafferty    14 年前

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