3

所以我相当熟悉使用 Microsoft Source Annotation Language(VS 2012-2013 风格)来描述带有指针的函数契约。

不过,我很好奇的一件事是,对于带_In_ _Pre_defensive_注释的被调用者没有首先检查指针的情况,我希望得到与没有它不同的结果。[我们的许多遗留函数都希望这些参数的输入有效,但政策是要仔细检查。] 描述标记为防御而不是自我防御的函数是否存在静态分析错误?

文档中,

如果函数出现在信任边界,我们建议您使用 _Pre_defensive_ 注释。"defensive" 修饰符修改了某些注解,表明在调用点,接口应该被严格检查,但是在实现主体中它应该假设可能传递了不正确的参数。在这种情况下,信任边界处首选 In_Pre_defensive_,以指示尽管调用者在尝试传递 NULL 时会收到错误,但函数体将被分析为参数可能为 NULL,并且任何取消引用的尝试未先检查其是否为 NULL 的指针将被标记。

这是一个用于代码分析的小演示程序。我的所有 4 个函数都在静态分析中显示 C6387,但我希望看到一个额外的迹象,表明我的“防御”函数实际上并没有像fun0(). 添加此限定符有什么好处吗?它确实使声明变得混乱,所以如果它没有帮助,很难证明把它放在那里是合理的。

// SAL2013.cpp : Defines the entry point for the console application.

#include "stdafx.h"

// pre-defensive annotation, but no check
void fun0(_In_ _Pre_defensive_ int* pInt)
{
    auto oops = *pInt;
}

// not defensive, no check
void fun1(_In_ int* pInt)
{
    auto oops = *pInt;
}

// defensive check, but not annotated defensively
void fun2(_In_ int* pInt)
{
    if (pInt != nullptr)
    {
        auto oops = *pInt;
    }
}

// defensive, with annotation
void fun3(_In_ _Pre_defensive_ int* pInt)
{
    if (pInt != nullptr)
    {
        auto oops = *pInt;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    int* p = nullptr;
    fun0(p); // C6387 Invalid Parameter Value
    fun1(p); // C6387 Invalid Parameter Value
    fun2(p); // C6387 Invalid Parameter Value
    fun3(p); // C6387 Invalid Parameter Value

    return 0;
}

奖励问题:我在 sal.h 中也看到了一个_In_defensive_(annotes)注释,但我不明白如何使用它。

谢谢。

4

1 回答 1

3

AFAICT 目前在添加_defensive_注释时没有(公共)差异。但是,Microsoft 有他们在内部使用的额外和扩展的分析器。这些可能会使用注释,也可能在将来向公众发布。

所以这是一个权衡。如果您认为它们使声明过于混乱,那么删除它们并没有真正的危害(尽管请注意,_Use_decl_annotations_您只需将注释放在标题中)。另一方面,将来可能会使用它,并且它也可以用作有关预期用途的文档。

编辑:至于_In_defensive_(annotes),它允许您将_Pre_defensive_注释应用于所有注释(在 中给出annotes)。此外,这允许您将注释放在不同的位置,即

_In_defensive(_Pre_satisfies_(pInt != nullptr))
void fun3(int* pInt)
{
}
于 2015-06-23T20:19:54.677 回答