Visual Studio /analyze不好之处---漏报(三)

分析是一种强大的VisualC++特性,可以帮助发现bug。然而,它有时会在不存在问题的情况下发现问题。不幸的是,这些误报,就像这里报道的那样,使/分析变得不那么有用,因为严重的错误最终会被“狼”的叫声淹没。在这篇文章中,我描述了另一个令人沮丧的误报,它展示了一个编译器完全无法理解别名的例子。

VisualStudio2012中的/analyze不再对这些构造发出警告。但不幸的是,它们已经从假阳性变成了假阴性。在GetInt()示例中,如果注释掉了部分初始化,则不会出现警告,即使返回的是部分未初始化的对象。更好吗?我不确定。

这些测试都是用Visual Studio 2010 SP1,C/C++优化编译器版本16.00 .40219.01为80×86。

未初始化的数据

我们有一些代码可以从文件或网络流中读取并初始化各种变量。当我们需要初始化一个整数时,我们调用这个函数:

int GetInt(const char* p)
{
    int result;
    char* pDest = (char*)&result;
    pDest[0] = p[0];
    pDest[1] = p[1];
    pDest[2] = p[2];
    pDest[3] = p[3];
    return result; // warning C6001: Using uninitialized memory ‘result’
}

编译器无法或不愿意告诉我们已经写入了结果的所有四个字节,因此在返回结果时会发出警告。
有人可能会说编译器在技术上是正确的。关于C++中的混叠的规则有点深奥,所以我相信这是合法的,但我不能肯定。使用联合是一种通知编译器正在发生别名的方法,因此我尝试:

    union intAlias
    {
        int i;
        char c[4];
    };

    int GetIntFixAttempt(const char* p)
    {
        intAlias result;
        char* pDest = result.c;
        pDest[0] = p[0];
        pDest[1] = p[1];
        pDest[2] = p[2];
        pDest[3] = p[3];
        return result.i; // warning C6001: Using uninitialized memory ‘result’
    }

没有骰子。但是我走的是正确的道路。接下来,我尝试像这样重写代码,使编译器更容易跟踪:

    union intAlias
    {
        int i;
        char c[4];
    };

    int GetIntFixed(const char* p)
    {
        intAlias result;
        result.c[0] = p[0];
        result.c[1] = p[1];
        result.c[2] = p[2];
        result.c[3] = p[3];
        return result.i; // No warning!
    }

终于成功了!很高兴知道,我可以找到代码,让/analyze意识到我没有使用未初始化的内存,但我不想经历这样的体操。所有关于代码将如何执行的信息都是可用的,所以没有理论上的原因说明编译器不能正确地推理,但是它失败了。
如果编译器想给我一个关于错误别名的警告,那么我可以尊重它,但是它当前的警告是错误的,这使得/analyze更难使用。

 

posted on 2020-08-05 11:53  活着的虫子  阅读(193)  评论(0编辑  收藏  举报

导航