如果忽略返回值,如何提出警告?

时间:2010-01-11 15:33:04

标签: c++ c gcc static-analysis

我想看看我的代码(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 个答案:

答案 0 :(得分:54)

您需要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);

答案 1 :(得分:10)

据我所知,没有GCC选项可以发出此警告。但是,如果您对特定功能感兴趣,可以使用以下属性标记它们:

int fn() __attribute__((warn_unused_result));

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

答案 2 :(得分:10)

您可以使用这个方便的模板在运行时执行此操作。

不返回错误代码(例如HRESULT),而是返回一个return_code&lt; HRESULT&gt;,如果超出范围而没有读取值,则返回该断言。它不是一个静态分析工具,但它仍然有用。

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;
};

答案 3 :(得分:7)

对于C ++ 17,由于我们现在拥有[[nodiscard]]属性,因此该问题的答案有所变化。在[dcl.attr.nodiscard]中涵盖:

  

可以将attribute-token nodiscard应用于函数声明中的declarator-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
  ^ ~

答案 4 :(得分:4)

任何静态分析代码(例如PC-Lint)都应该能够告诉您。对于PC-Lint,我知道情况确实如此。

答案 5 :(得分:4)

静态分析器将为您完成这项工作,但如果您的代码库更多,那么准备不堪重负; - )

答案 6 :(得分:4)

静态分析仪将是您最好的选择。我们在这里使用Coverity,但也可以使用free tools

如果你需要一个快速而肮脏的解决方案并且你有一个方便的Linux风格的外壳,你可以尝试类似的东西:

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

这将找到引用指定函数但不包含“=”的每一行。您可能会得到很多误报(可能还有一些误报),但如果您没有静态分析仪,那么它就是一个不错的起点。

答案 7 :(得分:2)

经典的'lint'程序过去常常对返回被忽略的值的函数非常感兴趣。麻烦的是,许多警告是不必要的 - 导致皮棉输出中的噪音过大(它正在捡起你想要忽略的一点点绒毛)。这可能就是GCC没有标准警告的原因。

另一个问题 - 另一方面 - “如果你知道自己忽略了结果但是真的不在意,你怎么压制警告”。经典场景是:

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

你关心signal()的第一个结果;你知道第二个是SIG_IGN(因为你只是设置它)。为了摆脱这些警告,我有时会使用一些变体:

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

这两次分配给old。您可以使用'assert(old == SIG_IGN)'来跟随它。