【问题标题】:Controlling the fill value (e.g. 0xCC) for uninitialized stack variables?控制未初始化堆栈变量的填充值(例如 0xCC)?
【发布时间】:2011-12-20 20:47:17
【问题描述】:

首先,一些背景知识:我们有一个框架将多次运行一系列测试,并确保每次运行时的状态相同。这捕获了许多产生不确定行为的情况,包括由多线程或指针值排序引起的情况。这些测试在 Visual Studio 2008 上的 Debug 中运行(即将移至 2010 年)。

问题:不幸的是,测试并没有像我希望的那样经常捕捉到未初始化变量的使用。考虑以下情况:

struct Foo{ int m_a; int m_b; };
void doStuff( struct Foo& f);
...
Foo* bar = new Foo();
// Uninitialized in ctor, but heap initialized to 0xCD,
// so appears "deterministic"
if (bar->m_a) 
{ ... }

Foo baz;
// may or may not initialize all of baz
// uninitialized members are left to 0xCC
doStuff( baz );
if (baz.m_b)
{ ... }

我想做的是在每次运行时使未初始化的值不同以捕获这些情况,例如第一次运行时已知垃圾,第二次运行时为 0。这样,对未初始化成员的任何计算都会给出不同的结果,并且在 if 语句中检查它们也将采用相反的分支。

我可以控制第一种情况,因为我们通过自己的堆路由 newdelete。但是,我无法找到有关如何控制堆栈变量的填充值的任何信息 - 这可能吗?我可以在这里找到的最接近的问题是Can g++ fill uninitialized POD variables with known values?,但这是针对 g++ 的。我不需要便携式解决方案;一个特定于 Visual Studio 的技巧就可以了。

注意 #1:我知道 lint/Rational Purify/Valgrind/[insert static code analysis magic bullet] 会捕捉到这一点,而且可能更健壮。但我正在寻找可以对现有框架进行的小改动,这些改动可能需要比我准备花费更多的时间来集成,所以请不要建议这些。

注意 #2:我们已经将警告级别设置为最大值并在打开错误时发出警告,这会捕获一些未初始化变量的情况,但这并不能捕获“doStuff”函数忘记初始化的所有情况一些结构。

注意 #3:我不太担心性能,因为它已经在 Debug 中运行,并且仅用于内部测试。

注意 #4:相同的可执行文件用于测试(在“写入”模式下运行一次,然后在“检查”模式下再次运行以比较结果),因此很遗憾,目前无法选择不同的编译设置。

提前致谢!

【问题讨论】:

  • 更改它不是一种选择。导致不匹配的一个明显方法是将 Debug 构建的结果与没有 /RTC 生效的构建进行比较。就像发布版本一样,真正需要测试的版本。
  • 系统当前的结构是相同的可执行文件运行每个测试。我应该在之前提到过。
  • 给所有东西一个默认构造函数,将每个成员设置为MAGIC_VAL 不是最简单的吗?并使用设置为不同值的MAGIC_VAL 进行几次调试测试。
  • 或者,/RTC1 flag
  • @MooingDuck - 看起来 /RTC1 仍然遗漏了一些基本案例。它是否捕捉到了 4 级警告(我们已经拥有)没有捕捉到的任何东西?

标签: c++ visual-studio visual-c++ memory


【解决方案1】:

您可以尝试找出执行填充的代码并在运行时对其进行修补。如果 dllversion 是固定的,这对于调试版本应该是可以接受的。您可能需要能够处理不同的 dll 加载地址,因为出于安全原因,它们可能会在随机位置加载。

【讨论】:

    【解决方案2】:

    我发现编译器在追踪这类问题方面非常有帮助:

    class checked_int_t {
        int i_;
    public:
        checked_int_t(int const i) : i_ (i) { }
        operator int() const { return i_; }
    };
    

    现在您的代码可能会更改为以下内容,当您分配 Foo 对象时会产生编译时错误,因为 m_a 未在默认构造函数中初始化。

    struct Foo{
        checked_int_t m_a;
        int m_b;
    };
    

    请注意,您可以逐步执行此操作来检查未初始化的变量,这样您就不必进行大的更改。只需找到您认为未正确初始化的变量并更改其类型即可。如果您绝对必须稍后设置该值,您也可以执行以下操作:

    class checked_int_t {
        int i_;
        bool set_;
    public:
        checked_int_t() : set_ (false) { }
        checked_int_t(int const i) : i_ (i), set_ (true) { }
        operator int() const { assert(set_); return i_; }
    };
    

    您可以使用条件编译来摆脱发布模式的类。

    #ifdef NDEBUG
    typedef int checked_int_t;
    #else
    class checked_int_t {
        int i_;
    public:
        checked_int_t(int const i) : i_ (i) { }
        operator int() const { return i_; }
    };
    #endif
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-15
      • 1970-01-01
      • 1970-01-01
      • 2012-01-14
      • 2013-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多