代码之家  ›  专栏  ›  技术社区  ›  Ofek Shilon

控制CRT内存初始化

  •  0
  • Ofek Shilon  · 技术社区  · 15 年前

    有时,您会遇到仅在发布版本和/或某些计算机上可复制的错误。一个常见的(但绝不是唯一的)原因是未初始化的变量,它们受随机行为的影响。例如,在大多数机器上,未初始化的bool在大多数情况下都可以为true,但随机地被初始化为false。

    我希望有一个系统化的方法,通过修改crt内存初始化的行为来清除这些错误。我很清楚MS调试CRT magic numbers -至少我希望有一个触发器将0xCDCDCDCD(初始化新分配内存的模式)变为零。我怀疑有人很容易就把烟抽出来 这样初始化有害程序,即使在调试版本中也是如此。

    我是否缺少一个可用的crt钩子(api,注册表项,等等)来启用它?有人有其他的想法去那里吗?

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

    1. 通常的幻数确实有许多优点,但它们不提供bool初始化(总是真的)的覆盖范围,也不提供针对单个位掩码或类似情况测试的位字段。一致的零初始化(当然,我可以打开和关闭它)将添加一层测试,它可以暴露出不好的init行为,否则这种情况很少见。
    2. 我当然知道 CrtSetAllocHook . 这样设置的钩子不会收到指向分配的缓冲区的指针(它被称为 之前 已经分配了这样的缓冲区),因此无法覆盖它。重载global new也没有多大好处,因为它会覆盖任何有效的构造函数初始化。

    [编辑:] @迈克尔,不知道你说的“超越新”是什么意思。简单的代码-

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

    不会起作用的。粘贴和修改整个::新代码可能可以工作,但看起来有点吓人(上帝只知道我必须包含和链接什么才能让它运行)。

    3 回复  |  直到 15 年前
        1
  •  3
  •   Michael Burr    15 年前

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

    msvc的调试生成使用的值是专门为帮助导致容易检测到的故障而设计的:

    • 它们不是0,因此针对未初始化内存的空指针检查不会隐藏错误
    • 它们不是有效的指针,因此取消对未初始化指针的引用将导致访问冲突。
    • 它们不是“通常”的整数值,因此,涉及未初始化数据的计算通常会导致极不正确的结果,而这些结果往往会导致明显的失败(我认为作为有符号数据处理时为负数也有助于解决这一点,但不仅仅是不寻常的数字)。

    而且,它们在调试器的数据显示中很容易识别。零并没有那么突出。

    综上所述,msvc提供了许多调试钩子/api,您可以使用这些钩子/api按照您想要的方式执行一些操作:

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

    您可以使用第三方调试分配库,如dmalloc( http://dmalloc.com/ )但我真的不知道这些库集成到msvc项目中有多容易,尤其是“现实世界”的项目。

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

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

    另外,您可能需要考虑迁移到visual studio 2010—当您使用未初始化的本地变量时,它将进入调试器—除了在调试器下运行调试生成之外,不做任何特殊的事情。当然,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的快速版本也支持这一点。

        2
  •  1
  •   John Deters    15 年前

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

        3
  •  1
  •   Ofek Shilon    15 年前

    进入crt显示幻数在heap alloc dbg和realloc帮助中使用,值本身被编码为

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

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

    它确实有效,但我会让这个问题保持一段时间-我仍然希望有人有更好的主意…我希望使用受控初始化运行自动测试,而不必手动执行(并且只使用可用的调试器)。