众所周知,Visual C++运行时标记未初始化或仅释放具有特殊非零标记的内存块。有没有办法完全禁用此行为,而无需手动将所有未初始化的内存设置为零?由于0xFEEEFEEE != 0
。
嗯,也许我应该更好地解释一下。我创建并初始化一个变量(通过new),一切都很好。当我释放它(通过删除)时,它将指针设置为0xFEEEFEEE
而不是NULL
。当我为NULL
插入适当的检查时,由于所有管理自己内存的好程序都应该,我会遇到问题,因为0xFEEEFEEE
通过NULL
检查没有问题。有没有什么好的方法,除了在删除它们时手动设置所有指针NULL
,以检测何时已释放内存?我宁愿不使用Boost只是因为我不想要开销,尽管可能很小,因为这是我唯一使用Boost的东西。
答案 0 :(得分:18)
创建指针时,explicity将其初始化为NULL
。同样在delete
之后。取决于未初始化数据的值(除少数特定情况外)是在寻找麻烦。
使用智能指针类(例如boost::shared_ptr
)可以自动处理指针是否初始化,从而为自己省去了很多麻烦。
答案 1 :(得分:14)
VC ++的行为不应该导致你可以做的任何有效检查造成严重破坏。如果你看到0xfeeefeee然后你没有写入内存(或已经释放它),所以你不应该从内存中读取。
答案 2 :(得分:8)
如果您正在阅读未初始化的内存,那么您的检查肯定不是“有效”。内存被释放。它可能已经被用于其他东西。你不能对C / C ++中未初始化的内存的内容做任何假设。
Java(和C#,我相信)将保证在使用之前已分配的内存为零,当然垃圾收集会阻止您看到释放的内存。但这不是C堆的属性,它直接暴露内存。
答案 3 :(得分:6)
如果您在Release模式而不是Debug模式下构建,则运行时根本不会填充未初始化的内存,但它仍然不会为零。但是,您应该不依赖于此行为 - 您应该使用memset(),ZeroMemory()或SecureZeroMemory()自己显式初始化内存,或者在某处设置一个标志,指示内存不是尚未初始化。读取未初始化的内存将导致未定义的行为。
答案 4 :(得分:6)
delete
的责任不是将对象的所有指针重置为NULL
。
此外,您不应该更改Windows DEBUG运行时的默认内存填充,您应该使用boost::shared_ptr<>
之类的东西作为指针。
那就是说,如果你真的想用脚射击自己,你可以。
您可以使用像这样的分配器挂钩更改 Windows DEBUG运行时的默认填充。这只适用于HEAP分配的对象!
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;
}
答案 5 :(得分:5)
你说:
我创建并初始化一个变量(通过new),一切都很好。当我释放它(通过删除)时,它将指针设置为0xFEEEFEEE而不是NULL。当我插入一个正确的NULL检查,因为所有管理自己内存的好程序都应该,我会遇到问题,因为0xFEEEFEEE会毫无问题地通过NULL检查。
即使MSVC的调试堆例程也不会更改您正在删除的指针的值 - 您要删除的指针的值不会更改(甚至为NULL)。听起来你正在访问一个属于你刚刚删除的对象的指针,这是一个简单明了的bug。
我很确定您尝试做的事情只会掩盖无效的内存访问。您应该发布一段代码来向我们展示实际发生的事情。
答案 6 :(得分:4)
这实际上是VC ++中的一个非常好的功能(我相信其他编译器),因为它允许您在调试器中查看指针的未分配内存。在禁用该功能之前,我会三思而后行。当您在C ++中删除对象时,您应该将指针设置为NULL
,以防稍后尝试再次删除该对象。此功能可让您找到忘记将指针设置为NULL
的位置。
答案 7 :(得分:4)
@Jeff Hubbard(comment):
这实际上无意中为我提供了我想要的解决方案:我可以在_HOOK_FREE上将pvData设置为NULL,而不会遇到0xFEEEFEEE的指针地址问题。
如果这对您有用,则表示您在测试NULL指针时正在读取释放的内存(即,指针本身位于您释放的内存中)。
这是一个错误。
你正在使用的'解决方案'只是隐藏而不是修复错误。当释放的内存被分配给其他内容时,突然间你将使用错误的值作为指向错误内容的指针。
答案 8 :(得分:4)
如果它在释放模式下工作,那是因为剪切运气。
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
正如几乎所有其他海报所说的那样,在调用NULL
后,指针应设置为delete
。无论你是自己动手还是使用boost或其他包装器,甚至是这个线程中的宏都取决于你。
答案 9 :(得分:3)
发生的事情是我的代码崩溃了 在调试编译下,但是 在发布汇编下成功。
发布版本将在客户的计算机上崩溃。它总是这样。
在您调用删除后,我在调试器下检查过它 我的指针正在设定 我打电话给删除后0xFEEEFEEE 它们。
指针不会更改。这是他们指向的内存设置为0xfeeefeee,0xfeeefeee,...,0xfeeefeee。
如果发现你的程序从释放的内存中读取数据(在DEBUG版本中由0xfeeefeee模式方便地指示),那么你就有错误。
答案 10 :(得分:1)
@ [Jeff Hubbard]:
正在发生的事情是我的代码在调试编译下崩溃,但在发布编译下成功。我在调试器下检查了它,并且在我调用delete之后我的指针被设置为
0xFEEEFEEE
。同样,发布时的相同代码不会崩溃并且行为与预期一致。
这是一种非常奇怪的行为 - 我仍然确信_CrtSetAllocHook()
解决方法可能隐藏了潜在的错误。
操作系统堆管理器使用0xFEEEFEEE
签名来指示释放的内存(请参阅http://www.nobugs.org/developer/win32/debug_crt_heap.html)。你有没有机会发布一些repro代码并准确指出你正在使用哪个编译器版本?
答案 11 :(得分:0)
我很确定你不能在这里禁用visual studio默认设置,即使你这样做了,那么该值也就是内存分配之前的内存。
你最好养成将它们设置为0的习惯,它只有2个额外的字符。
int *ptr=0;
你也可以使用NULL宏,它被定义为0(但不是默认的,因此当包含像windows.h这样的东西并自己定义它时,请使用多个定义进行处理!
答案 12 :(得分:0)
如果您使用的是malloc,它不会将内存初始化为任何内容。你得到了什么。如果你想分配一个块并将其初始化为0,那么使用'calloc'就像malloc一样只有初始化(如果要模拟malloc,则设置为1的元素大小参数)。你应该在使用之前阅读calloc,因为它有一些细微的差别。
http://wiki.answers.com/Q/What_is_the_difference_between_malloc_and_calloc_functions
答案 13 :(得分:0)
为什么不创建自己的#define并养成使用它的习惯?
即
#define SafeDelete(mem) { delete mem; mem = NULL; }
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }
显然你可以随意命名。 deleteZ,deletesafe,无论你喜欢什么。
答案 14 :(得分:0)
您也可以创建一个内存管理器。然后你可以覆盖new和delete来拉出/放回预先分配的内存。