0xDEADBEEF与NULL

时间:2011-05-06 06:29:26

标签: c++ c pointers null

在各种代码中,我已经在使用NULL ...

的调试版本中看到了内存分配
memset(ptr,NULL,size);

0xDEADBEEF ...

memset(ptr,0xDEADBEEF,size);
  1. 使用每一个有什么好处,在C / C ++中实现这一目标的首选方法是什么?
  2. 如果为指针指定了0xDEADBEEF的值,它是否仍然不符合有效数据?

10 个答案:

答案 0 :(得分:57)

  1. 使用memset(ptr, NULL, size)memset(ptr, 0xDEADBEEF, size)清楚地表明作者不明白他们在做什么。

    首先,如果将memset(ptr, NULL, size)定义为整数零,NULL将确实将C和C ++中的内存块清零。

    但是,在此上下文中使用NULL表示零值是不可接受的做法。 NULL是专门为指针上下文引入的宏。 memset的第二个参数是整数,而不是指针。将内存块清零的正确方法是memset(ptr, 0, size)。注意:0不是NULL。我会说即使memset(ptr, '\0', size)看起来比memset(ptr, NULL, size)更好。

    此外,最新的(目前)C ++标准--C ++ 11 - 允许将NULL定义为nullptrnullptr值不能隐式转换为类型int,这意味着上述代码无法保证在C ++ 11及更高版本中进行编译。

    在C语言中(您的问题也标记为C),宏NULL可以扩展为(void *) 0。即使在C (void *) 0中也不能隐式转换为int类型,这意味着通常情况下memset(ptr, NULL, size)只是C中的无效代码。

    其次,即使memset的第二个参数具有类型int,该函数也会将其解释为unsigned char值。这意味着只使用该值的一个低字节来填充目标内存块。出于这个原因,memset(ptr, 0xDEADBEEF, size)将编译,但不会用0xDEADBEEF值填充目标内存区域,因为代码的作者可能天真地希望。 memset(ptr, 0xDEADBEEF, size)memset(ptr, 0xEF, size)等效(假设为8位字符)。虽然这可能足以用有意的“垃圾”填充一些内存区域,但memset(ptr, NULL, size)memset(ptr, 0xDEADBEEF, size)之类的东西仍然背叛了作者的主要缺乏专业性。

    同样,正如其他答案已经指出的那样,这里的想法是用“垃圾”值填充未使用的内存。在这种情况下,零肯定不是一个好主意,因为它不够“丰富”。使用memset时,您只能使用一个字节的值,例如0xAB0xEF。如果这对您的目的而言足够好,请使用memset。如果您想要更具表现力和唯一性的垃圾值,例如0xDEDABEEF0xBAADFOOD,您将无法使用memset。您必须编写一个专用函数,可以用4字节模式填充内存区域。

  2. C和C ++中的指针不能分配任意整数值(Null指针常量除外,即为零)。只能通过使用显式强制转换将积分值强制转换为指针来实现此类赋值。从形式上讲,这种演员表的结果是实现定义的。结果值当然可以指向有效数据。

答案 1 :(得分:10)

0xDEADBEEF或其他非零位模式是一个好主意,能够捕获写后删除和读后删除使用。

1)删除后写入

通过编写特定模式,您可以检查已经解除分配的块是否稍后被错误代码写入;在我们的调试内存管理器中,我们使用一个空闲的块列表,在回收内存块之前,我们检查我们的自定义模式是否仍在整个块中写入。当然,当我们发现问题时,它有点“迟到”,但仍然比发现不进行检查时更早。 我们还有一个特殊的函数,它被定期调用,也可以按需调用,只需要遍历所有释放的内存块列表并检查它们的一致性,因此我们可以在追逐错误时经常调用此函数。使用0x00000000作为值将不会有效,因为零可能正好是错误代码想要在已经解除分配的块中写入的值,例如将字段归零或将指针设置为NULL(相反,错误代码更不可能写入0xDEADBEEF)。

2)删除后读取

保留解除分配的块的内容不变或甚至只写零会增加读取死存储器块内容的人仍然会发现合理的值并与不变量兼容的可能性(例如,许多架构上的NULL指针NULL只是二进制零,或整数0,ASCII NUL char或双精度值0.0)。 通过编写“奇怪”模式,如0xDEADBEEF大多数将在读取模式下访问的代码,这些字节可能会找到奇怪的不合理值(例如整数-559038737或双值,值-1.1885959257070704e + 148),希望触发一些其他自我一致性检查断言。

当然没有什么特定于位模式0xDEADBEEF,实际上我们对释放的块使用不同的模式,块前区域,块后区域以及我们的内存管理器写入另一个(地址相关的)任何内存块的内容部分的特定位模式,然后将其提供给应用程序(这有助于查找未初始化内存的使用)。

答案 2 :(得分:9)

我肯定会推荐0xDEADBEEF。它清楚地标识未初始化的变量,并访问未初始化的指针。

奇怪的是,取消引用0xdeadbeef指针肯定会在加载单词时在PowerPC体系结构上崩溃,并且很可能在其他体系结构上崩溃,因为内存很可能在进程的地址空间之外。

归零内存是一种方便,因为许多结构/类具有使用0作为其初始值的成员变量,但我非常建议初始化构造函数中的每个成员而不是使用默认的内存填充。您真的希望了解是否正确初始化变量。

答案 3 :(得分:6)

http://en.wikipedia.org/wiki/Hexspeak

这些“魔术”数字是用于识别错误指针,未初始化内存等的调试辅助工具。您需要在正常执行期间不太可能发生的值以及在执行内存转储或检查变量时可见的值。在这方面,初始化为零不太有用。我猜想当你看到人们初始化为零时,这是因为他们需要将该值设置为零。值为0xDEADBEEF的指针可能指向有效的内存位置,因此将其用作NULL的替代方案是个坏主意。

答案 4 :(得分:4)

将缓冲区空闲或将其设置为特殊值的一个原因是,您可以轻松判断缓冲区内容在调试器中是否有效

取消引用值为“0xDEADBEEF”的指针几乎总是危险的(可能会破坏您的程序/系统),因为在大多数情况下,您不知道存储在那里的内容。

答案 5 :(得分:2)

DEADBEEF是HexSpeek的一个例子。有了它,作为程序员,你会故意传达一个错误条件。

答案 6 :(得分:1)

我个人建议使用NULL(或0x0),因为它表示预期的NULL,并在比较时派上用场。想象一下你正在使用char *和DEADBEEF之间出于某种原因(不知道为什么),那么至少你的调试器会非常方便地告诉你它的0x0。

答案 7 :(得分:1)

我会选择NULL,因为将内存质量调零比后来更容易并将所有指针设置为0xDEADBEEF要容易得多。此外,什么都没有阻止0xDEADBEEF成为x86上的有效内存地址 - 诚然,这将是不寻常的,但远非不可能。 NULL更可靠。

最终,look- NULL是语言惯例。 0xDEADBEEF看起来很漂亮,就是这样。你没有得到任何东西。库将检查NULL指针,它们不检查0xDEADBEEF指针。在C ++中,零指针的概念甚至没有绑定到零值,只用字面零表示,而在C ++ 0x中有一个nullptr和一个nullptr_t

答案 8 :(得分:0)

如果这对StackOverflow来说太过于舆论,请投反对票,但我认为整个讨论是我们用来制作软件的工具链中一个明显漏洞的症状。

通过使用“garabage-y”值初始化内存来检测未初始化的变量只能检测某些类型数据中的某些错误。

在调试版本中检测未初始化的变量而不是发布版本就像是在测试飞机并且告诉飞行公众满意“好,它测试正常”时遵循安全程序。 / p>

我们需要硬件支持来检测未初始化的变量。就像在存储器的每个可寻址实体(=大多数机器上的字节)附带的“无效”位一样,并且由OS在每个字节中设置VirtualAlloc()(等等,或其他OS的等价物)手转到应用程序,并在写入字节时自动清除,但如果先读取则会导致异常。

内存足够便宜,处理器速度足够快。这种依赖于“有趣”模式的结束,让我们都诚实地开机。

答案 9 :(得分:0)

请注意memset中的第二个参数应该是一个字节,即它被隐含地强制转换为char或类似字符。对于大多数平台,0xDEADBEEF会转换为0xEF(对于某些奇怪的平台,还会转换为其他内容)。

另请注意,第二个参数应该正式为intNULL不是

现在有利于进行这些初始化。当然,首先行为更可能是确定性的(即使我们最终以未定义的行为结束,行为在实践中也会保持一致)。

具有确定性行为将意味着调试变得更容易,当您发现错误时,您只会"必须提供相同的输入,故障将表现出来。

现在,当您选择要使用的值时,应选择最有可能导致不良行为的值 - 这意味着使用未初始化的数据更有可能导致观察到错误。这意味着您将不得不使用相关平台的一些知识(但是其中许多行为非常相似)。

如果内存用于保存指针,那么确实已经清除了内存将意味着你得到一个NULL指针并且通常解除引用会导致分段错误(这将被视为故障)。但是,如果您以其他方式使用它,例如作为算术类型,那么您将获得0并且对于许多不是奇数的应用程序。

如果您改为使用0xDEADBEEF,您将获得一个非常大的整数,同样在将数据解释为浮点时,它也将是一个非常大的数字(IIRC)。如果将其解释为文本,它将非常长并包含非ascii字符,如果使用UTF-8编码,则可能无效。现在,如果在某些平台上用作指针,它将失败某些类型的对齐要求 - 在某些平台上也可能无法映射出内存区域(请注意,在x86_64上,指针的值将为0xDEADBEEFDEADBEEF,这是超出地址范围)。

请注意,虽然填充0xEF将具有非常相似的属性,但如果您想使用0xDEADBEEF填充内存,则需要使用自定义函数,因为memset没有&#39}做这个伎俩。