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

如果不考虑返回值,如何发出警告?

  •  50
  • Drakosha  · 技术社区  · 15 年前

    我想看看我的代码(C++)中忽略函数返回值的所有地方。我该怎么做呢——使用GCC或静态代码分析工具?

    错误代码示例:

    int f(int z) {
        return z + (z*2) + z/3 + z*z + 23;
    }
    
    
    int main()
    {
      int i = 7;
      f(i); ///// <<----- here I disregard the return value
    
      return 1;
    }
    

    请注意:

    • 即使函数和它的使用在不同的文件中,它也应该工作
    • 自由的 静态检查工具
    8 回复  |  直到 6 年前
        1
  •  53
  •   Eric Seppanen    15 年前

    你想要GCC的 warn_unused_result 属性:

    #define WARN_UNUSED __attribute__((warn_unused_result))
    
    int WARN_UNUSED f(int z) {
        return z + (z*2) + z/3 + z*z + 23;
    }
    
    int main()
    {
      int i = 7;
      f(i); ///// <<----- here i disregard the return value
      return 1;
    }
    

    尝试编译此代码会产生:

    $ gcc test.c
    test.c: In function `main':
    test.c:16: warning: ignoring return value of `f', declared with
    attribute warn_unused_result
    

    您可以在 Linux kernel 他们有一个 __must_check 执行相同操作的宏;看起来您需要GCC 3.4或更高版本才能工作。然后您将发现内核头文件中使用的宏:

    unsigned long __must_check copy_to_user(void __user *to,
                                            const void *from, unsigned long n);
    
        2
  •  10
  •   anon    15 年前

    据我所知,没有GCC的选择来给出这个警告。但是,如果您对特定的函数感兴趣,可以用属性标记它们:

    int fn() __attribute__((warn_unused_result));
    

    如果不使用fn()的返回值,则会发出警告。警告:我自己从未使用过这个功能。

        3
  •  10
  •   JoeG    15 年前

    您可以在运行时使用这个方便的模板来完成这项工作。

    不是返回错误代码(例如,hresult),而是返回一个返回代码<hresult>,它断言如果它超出范围而没有读取值。它不是一个静态分析工具,但也同样有用。

    class return_value
    {
    public:
      explicit return_value(T value)
        :value(value), checked(false)
      {
      }
    
      return_value(const return_value& other)
        :value(other.value), checked(other.checked)
      {
        other.checked = true;
      }
    
      return_value& operator=(const return_value& other)
      {
        if( this != &other ) 
        {
          assert(checked);
          value = other.value;
          checked = other.checked;
          other.checked = true;
        }
      }
    
      ~return_value(const return_value& other)
      {
        assert(checked);
      }
    
      T get_value()const {
        checked = true;
        return value;
      }
    
    private:
      mutable bool checked;
      T value;
    };
    
        4
  •  7
  •   Shafik Yaghmour    6 年前

    对于C++ 17,这个问题的答案改变了,因为我们现在有 [[nodiscard]] 属性。覆盖在 [dcl.attr.nodiscard] :

    属性标记nodiscard可以应用于函数声明中的声明器ID,也可以应用于类或枚举的声明。在每个属性列表中最多出现一次,不存在属性参数子句。

    [举例]:

    struct [[nodiscard]] error_info { /* ... */ };
    error_info enable_missile_safety_mode();
    void launch_missiles();
    void test_missiles() {
      enable_missile_safety_mode(); // warning encouraged
      launch_missiles();
    }
    error_info &foo();
    void f() { foo(); }             // warning not encouraged: not a nodiscard call, because neither
                                    // the (reference) return type nor the function is declared nodiscard
    

    ____结束示例__]

    所以修改你的例子( see it live ):

    [[nodiscard]] int f(int z) {
        return z + (z*2) + z/3 + z*z + 23;
    }
    
    
    int main()
    {
      int i = 7;
      f(i); // now we obtain a diagnostic
    
      return 1;
    }
    

    我们现在获得了GCC和Clang的诊断,例如

    warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
      f(i); // now we obtain a diagnostic
      ^ ~
    
        5
  •  4
  •   Xavier Nodet    15 年前

    任何静态分析代码(例如 PC-Lint )应该能告诉你。对于pc lint,我知道情况就是这样。

        6
  •  4
  •   Alon    15 年前

    静态分析器将为您完成这项工作,但是如果您的代码库更为琐碎,那么就准备好被淹没;-)

        7
  •  4
  •   bta    15 年前

    静态分析器将是您的最佳选择。我们在这里使用隐蔽,但有 free tools 也可以使用。

    如果您需要一个快速而肮脏的解决方案,并且手头上有一个Linux风格的shell,您可以尝试以下方法:

    grep -rn "function_name" * | grep -v "="
    

    它将查找引用指定函数但不包含“=”的每一行。你可以得到很多误报(可能还有一些误报),但是如果你没有一个静态分析仪,这是一个不错的开始。

        8
  •  2
  •   Jonathan Leffler vy32    15 年前

    经典的“lint”程序过去对于返回被忽略值的函数非常易懂。问题是,这些警告中的许多都是不需要的——导致棉绒输出噪音过大(它收集了一些您希望忽略的绒毛)。这可能就是GCC没有标准警告的原因。

    另一个问题是,“当你知道你忽略了结果,但实际上并不关心时,你如何抑制警告?”典型的情况是:

    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
        signal(SIGHUP, sighandler);
    

    你关心的第一个结果来自 signal() 你知道第二个是西格伊恩(因为你刚刚设置了它)。为了避开警告,我有时会在以下方面使用一些变体:

    if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
        old = signal(SIGHUP, sighandler);
    

    这分配给 old 两次。您可以在后面加上“assert(old==sig_ign)”。