代码之家  ›  专栏  ›  技术社区  ›  sorin

如何在msvc中正确重写assert代码进行传递/分析?

  •  10
  • sorin  · 技术社区  · 15 年前

    visual studio添加的代码分析( /analyze 用于C/C++,以帮助识别坏代码。这是一个很好的特性,但是当你处理旧项目时,你可能会被大量的警告淹没。

    大多数问题都是由于旧代码在方法或函数的开头执行一些断言而产生的。

    我认为这是代码中使用的断言定义(来自 afx.h )

    #define ASSERT(f)          DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))
    

    示例代码:

    ASSERT(pBytes != NULL);
    *pBytes = 0; // <- warning C6011: Dereferencing NULL pointer 'pBytes'
    

    我正在寻找一个简单,干净和安全的解决方案来解决这些警告,而不是意味着禁用这些警告 . 我有没有提到在当前的代码库中有很多事件?

    6 回复  |  直到 12 年前
        1
  •  3
  •   John Dibling    15 年前

    prefast告诉你你的代码有缺陷,不要忽略它。你确实有一个,但你只是匆匆忙忙地知道它。问题是:仅仅因为 pBytes 从未在开发和测试中为空并不意味着它不会在生产中。你不能应付这种不测。prefast知道这一点,并试图警告您,生产环境是敌对的,并将留给您的代码一个冒烟,残缺的大量无用字节。

    /咆哮

    有两种方法可以解决这个问题:正确的方法,和黑客。

    正确的方法是在运行时处理空指针:

    void DoIt(char* pBytes)
    {
        assert(pBytes != NULL);
        if( !pBytes )
            return;
        *pBytes = 0;
    }
    

    这将使预演安静下来。

    技巧是使用注释。例如:

    void DoIt(char* pBytes)
    {
        assert(pBytes != NULL);
        __analysis_assume( pBytes );
        *pBytes = 0;
    }
    

    编辑: Here's a link 描述前置注释。无论如何,这是个起点。

        2
  •  5
  •   Stack Overflow is garbage    15 年前

    /analyze 不能保证发出相关和正确的警告。 它会漏掉很多问题,也会给出一些误报(它认为是警告,但这是完全安全的,永远不会发生)

    期望使用/分析零警告是不现实的。

    它指出了一种情况,即取消引用一个指针 无法验证始终有效。据prefast所知,不能保证它永远不会为空。

    但这并不意味着 可以 为空。只是证明它安全所需的分析对于prefast来说太复杂了。

    您可以使用特定于Microsoft的扩展 __assume 告诉编译器它不应该产生这个警告,但是更好的解决方案是留下警告。每次使用/analyze进行编译时(不一定每次编译时都是这样),都应该验证它确实出现的警告仍然是误报。

    如果正确使用断言(在编程过程中捕捉逻辑错误,防止出现 不能 如果是这样的话,我看你的代码没有问题,或者留下警告。添加代码来处理 可以 从不发生是没有意义的。你无缘无故地增加了更多的代码和复杂性(如果它永远不会发生,那么你就无法从中恢复,因为你完全不知道程序将处于什么状态。你只知道它进入了一个你认为不可能的代码路径。

    但是,如果使用断言作为实际的错误处理(值 可以 在特殊情况下为空,只是希望它不会发生),那么它就是代码中的一个缺陷。然后需要正确的错误处理(通常是异常)。

    永远不要用断言来解决以下问题 可能的 . 使用它们来验证 不可能的 不会发生的。当/analyze给出警告时,看看它们。如果是假阳性,就忽略它(不要抑制它,因为虽然今天是假阳性,但明天签入的代码可能会将它变成真正的问题)。

        3
  •  3
  •   carpenterjc    12 年前

    首先,断言语句必须保证抛出或终止应用程序。在本例中,经过一些实验,我发现/analyse忽略了模板函数、内联函数或普通函数中的所有实现。相反,您必须使用宏和do{}while(0)技巧,内联抑制

    如果你看一下 ATLENSURE (Microsoft在宏中使用analyze assume(),他们还提供了几段非常好的文档,说明为什么以及如何迁移ATL以使用此宏。

    作为一个例子,我修改了cppunit_assert宏,以同样的方式清除单元测试中的数千个警告。

    #define CPPUNIT_ASSERT(condition)                                                                    \
    do {    ( CPPUNIT_NS::Asserter::failIf( !(condition),                                                \
                                      CPPUNIT_NS::Message( "assertion failed" ),                         \
                                      CPPUNIT_SOURCELINE() ) ); __analysis_assume(!!(condition));        \
                                      __pragma( warning( push))                                          \
                                      __pragma( warning( disable: 4127 ))                                \
                                      } while(0)                                                         \
                                      __pragma( warning( pop))
    
        4
  •  0
  •   Michael Howard-MSFT    15 年前

    请记住,assert()在零售版本中消失,因此上面代码中的c601警告是绝对正确的:您必须检查pbytes是否为非空,并执行assert()操作。assert()只是在调试错误中满足条件时将应用程序抛出到调试器中。

    我做了大量的分析和准备工作,所以如果你还有其他问题,请随时告诉我。

        5
  •  -1
  •   MSalters    15 年前

    你似乎认为 ASSERT(ptr) 不知怎么的意思 ptr 之后不为空。这不是真的,代码分析器也没有这样的假设。

        6
  •  -2
  •   Michael Howard-MSFT    15 年前

    我的合著者David LeBlanc会告诉我,这个代码被破坏了,假设你使用C++,你应该使用引用而不是指针,而REF不能为null: