【问题标题】:Controling CRT memory initialization控制 CRT 内存初始化
【发布时间】:2010-05-04 21:56:07
【问题描述】:

有时您会遇到仅在发布版本中和/或仅在某些机器上可重现的错误。一个常见的(但绝不是唯一的)原因是未初始化的变量,它们受到随机行为的影响。例如,在大多数机器上,未初始化的 BOOL 在大多数情况下可能为 TRUE,但随机初始化为 FALSE。

我希望通过修改 CRT 内存初始化的行为来清除此类错误的系统方法。我很清楚 MS 调试 CRT magic numbers - 至少我希望有一个触发器将 0xCDCDCDCD(初始化新分配的内存的模式)设置为零。我怀疑一个人可以很容易地抽出讨厌的东西 以这种方式初始化害虫,即使在调试版本中也是如此。

我是否缺少启用此功能的可用 CRT 挂钩(API、注册表项等)?有人有其他想法吗?

[编辑:] 似乎需要澄清一下。

  1. 通常的幻数确实有很多优点,但它们不提供布尔初始化(始终为真)或针对单个位掩码进行测试的位字段或类似情况的覆盖范围。一致的零初始化(当然,我可以打开和关闭),会增加一层测试,以显示在其他情况下可能很少见的不良初始化行为。
  2. 我当然知道CrtSetAllocHook。这样设置的钩子不会接收到分配的缓冲区的指针(它被称为之前这样的缓冲区被分配),所以它不能覆盖它。重载全局 new 也没有什么好处,因为它会覆盖任何有效的构造函数初始化。

[编辑:] @Michael,不确定您所说的覆盖 new 是什么意思。简单的代码,例如 -

void* new(...)
{
   void* res = ::new(...);   // constructors now called!
   if(SomeExternalConditionApplies())
      OverWriteBufferWithMyPetValues(res);
}

行不通。粘贴和修改整个 ::new 代码可能会起作用,但似乎有点吓人(上帝只知道我必须#include 和链接才能运行它)。

【问题讨论】:

标签: visual-c++ memory-management msvcrt


【解决方案1】:

我没有关注 - 将未初始化的内存设置为 0xcdcdcdcd 而不是 0 是 更好 用于清除错误,因为代码更有可能获得“范围内”算术或特殊处理0. 使用非常无效的值,错误更有可能“快速失败”,因此可以修复而不是隐藏。

MSVC 的调试版本使用的值专门用于帮助导致可以轻松检测到的故障:

  • 它们不是 0,因此针对未初始化内存的 NULL 指针检查不会隐藏错误
  • 它们不是有效的指针,因此取消引用未初始化的指针会导致访问冲突
  • 它们不是“通常的”整数值,因此涉及未初始化数据的计算通常会导致非常不正确的结果,这往往会导致明显的故障(我认为当处理为签名数据时为负数也有助于解决这个问题,但是不仅仅是不寻常的数字)。

此外,它们在调试器的数据显示中很容易识别。零几乎没有那么突出。

总而言之,MSVC 提供了许多调试钩子/API,您可以使用它们来做一些您想做的事情:

针对您更新的问题的一些附加信息:

您也许可以使用像 Dmalloc (http://dmalloc.com/) 这样的第 3 方调试分配库,但老实说,我不知道将这些库集成到 MSVC 项目中有多容易,尤其是“现实世界”项目。

另外,请注意,这些显然只会处理动态分配(并且可能无法与 MSVC 的默认 new 实现很好地集成)。

可以使用 operator new() 的全局覆盖来处理在 C++ 中使用 new 发生的分配 - 覆盖任何有效的构造函数初始化都没有问题 - 发生 new 分配在构造函数执行任何初始化之前(如果您考虑一下,应该清楚为什么会这样)。

此外,您可能需要考虑迁移到 Visual Studio 2010 - 当您使用未初始化的局部变量时,它会中断调试器 - 除了在调试器下运行 Debug 构建之外没有什么特别的事情。当然,MSCV 已经对这些情况发出了一段时间的警告,但是 VS2010 会在调试器中捕获以下内容,这不会产生任何警告(至少在我当前的编译器设置下):

int main(  )
{
    unsigned int x;
    volatile bool a = false;

    if (a) {
        x = 0;
    }

    printf( "Hello world %u\n", x); // VS2010 will break here because x is uninitialized

    return 0;
}

即使是 VC++ 2010 的 Express 版本也支持这一点。

【讨论】:

  • 感谢 VS2010 的提示!我们确实在迁移,但它是一个庞大的代码库,而且需要一段时间。不确定重载 ::new 是什么意思 - 请参阅问题中的进一步编辑。
  • @Ofek:您的程序可以为各种全局 newdelete 运算符提供替换。这些函数负责在调用全局new 时分配/取消分配将为对象提供的内存。该对象没有被operator new() 函数初始化——这留给了构造函数。当然,如果班级有自己的operator new() 和朋友,这也无济于事。拥有一套称职的operator new()/delete() 替代品并不难,请参阅informit.com/guides/content.aspx?g=cplusplus&seqNum=40 以获得快速概览。
【解决方案2】:

只是一个建议:你能在你的编译器中使用静态代码分析工具吗? /analyze 会给你一个 C6001 警告你正在使用未初始化的内存。而且它有点系统,这正是您所要求的。

【讨论】:

  • 谢谢 - 但我们使用的是 VS2005,所以还没有集成 prefast。曾经有一个 hack 可以从 WDK 提供的编译器或其他东西中运行 prefast,但这是一个主要痛苦..
【解决方案3】:

进入 CRT 显示幻数在 _heap_alloc_dbg 和 realloc_help 中使用,并且值本身被编码为

static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects with this */

知道要搜索什么often helps。链接的线程确实有一个很好的建议:在 _bCleanLandFill 上设置一个监视并从调试器中修改它。

它确实有效,但我会暂时保留这个问题 - 我仍然希望有人有更好的想法......我希望通过受控初始化运行自动化测试,而不必手动进行(并且仅使用可用的调试器)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-13
    • 2015-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多