2015-07-21 146 views
1

我目前正在为我的C++代码的某些功能(它是我主要为了教育目的而编写的游戏引擎)编写一些基本测试。我想测试的功能之一是内存分配代码。如果代码处于调试模式,测试目前由一个运行每次启动的函数组成。这迫使我在调试时总是测试代码。测试C++代码和IsBadWritePtr

为了测试我的内存分配的代码我的直觉是做这样的事情:

int* test = MemoryManager::AllocateMemory<int>(); 
assert(!IsBadWritePtr(test, sizeof(int)), "Memory allocation test failed: allocate"); 

MemoryManager::FreeMemory(test); 
assert(IsBadWritePtr(test, sizeof(int)), "Memory free test failed: free"); 

此代码工作正常,但所有我能找到的资源都说不要使用IsBadWritePtr功能(这是一个WinAPI功能适用于那些不熟悉的)。在这种情况下使用此功能是否正常?对使用它的三个主要的警告,我发现是:

这可能会导致问题与防护页面

这不是一个问题,因为内存分配的代码就在那里,我知道我不分配保护页。

这是更好地失败较早

这不是因为我硬是用它来尽早失败的思考。

它不是线程安全的

测试代码是正确的,在执行的开始执行,很久以前任何其他线程存在。它也作用于没有其他指针创建的内存,因此不能存在于其他线程中。

所以基本上我想知道在这种情况下使用这个函数是否是一个好主意,以及如果有什么我失踪的函数。我也意识到指向错误位置的东西仍然会通过这个测试,但它至少能检测到大部分内存分配错误,对(如果分配失败,我有机会获得指向有效内存的机会?)

回答

2

我打算把它写成评论,但它太长了。

我会调出房间里的大象:为什么不以传统方式测试失败(通过返回null或抛出异常)?不要忘了IsBadWritePtr如此皱眉(甚至它的文档说它已经过时,你不应该使用它),但是你的用例看起来并不合适。从MSDN文档:

此函数通常用于处理从第三方库返回的指针,其中您无法确定第三方DLL中的内存管理行为。

但是你没有使用它来测试通过/从DLL返回的任何东西,你似乎正在使用它来测试分配成功,这不仅是不必要的(因为你已经知道从返回值HeapAlloc,GlobalAlloc等),但这不是IsBadWritePtr的意图。

此外,测试分配成功不是应该只在调试模式或断言中进行,因为它明显超出了您的控制范围,您无法通过调试来“修复”它。

+0

好的,这是有道理的。至于为什么这只发生在调试模式下,我的测试的一部分是分配器工作正常。它分配一个小内存,测试它被分配,释放它,然后测试它是免费的。这是为了确保分配器正在工作并提前失败,而不是等待它被使用。当我使用它时,代码中还有一些检查,但我认为将内存分配器放在测试之外是一件非常重要的事情。 –

0

建立在@ user1610015的答案,有一个原因为什么IsBadReadPtr不应该在您的方案中工作。

基本上IsBadReadPtr工作在整个页面粒度。这意味着上面的代码是正确的每个分配你都会占用整个页面(分钟4KB)。

现代分配器使用各种技巧将大量分配打包到页面中(低碎片桶堆,分配的链接列表等)。如果你不打包这样的小分配,那么认为像stl地图和其他库使用大量的小分配将绝对会杀死你的游戏(无论是在内存使用和高速缓存一致性将被这么多未使用的填充杀死的事实)。

作为一个方面,你最后的评论关于线程安全是危险的。您链接的许多应用程序和库可以使用全局对象构造函数(并在main调用之前运行)和其他技巧将代码插入到流程中。所以我肯定会检查这是你的代码现在的情况,但更重要的是,当你添加第三方库到你的代码检查它。

+0

谢谢,目前我的代码只依赖于Windows API,但如果我添加任何其他库,我会保留你所说的关于线程安全的内容。所讨论的内存是通过'VirtualAlloc'分配的,因此它被分配在页面边界上:'区域的大小,以字节为单位。如果lpAddress参数为NULL,则将该值向上舍入到下一页边界。“但我仍然同意你的看法,但这种用法是错误的,我大多只是对原因感到好奇。 –