没有值的警告 'return',在函数 returning non-void 中 - return 应该是什么?

Warning 'return' with no value, in function returning non-void - What should it return?

如何解决以下标题中的警告?

struct Nodes* InsertNode(unsigned int IP, unsigned short Port)
{
    if (!IP)
       return;
    if (!Port)
       return;
    // Above is what chucks the warnings
    {
        // do stuff & conditionally
           return &List[x];
    }
    // Different conditions & stuff
    {
       return &List[Other];
    }
}

也就是说,在通过缺失数据放弃的情况下,应该怎么办return?或者我是否需要浏览整个 body 代码并每次都检查是否应该调用它?如果我要继续使用它(或升级 OS 它是 运行 ),该程序只是 returning 在这一点上按预期运行,修复编译器警告似乎是个好主意,当编译器版本发生变化时,它们往往会变成错误。

this answer 中有一条线索可以回答有人询问同样的警告,但答案并没有给我足够的信息来继续,我读过的其他人也没有。

额外信息:检查 IPPort 的值是为了清理 &List 的内容,这种情况表明来自错误配置的客户端的数据报或来自有恶意的人的流量,令人遗憾但它发生了。这是我们根本不关心的无效数据,记录它似乎毫无意义,它不应该延迟处理下一个,绝对不能停止程序。在从 gcc 4.9 切换到 6.3 之前,我没有看到警告。当前的 return; 看起来 只是 black-hole 它,但我只理解代码的一些意图。

in the case of giving up through missing data, what should it return?

视情况而定。

有几种情况

  1. 函数没有设计成returnNULL作为有效值。

    替换

    if (!IP)
      return;
    if (!Port)
      return;
    

    来自

    if (!IP || !Port)
    {
      errno = EINVAL; /* Setting errno, allows the caller to log 
                         the failure using the perror() function. */
      return NULL;
    }
    

    这样使用:

    struct Nodes * p = InsertNode (...);
    if (NULL == p)
    {
       perror("InsertNode() failed");
       /* exit or error logging/handling */
    }
    
  2. IPPort 在正常操作下 永远不会 成为 0。因此,如果它们是,那将是一个编程错误。

    在这些情况下,您可能不会 return 而是结束程序。

    所以不用

    if (!IP)
      return;
    if (!Port)
      return;
    

    使用

    assert((IP) && (Port));
    

    此处无需特定用法,因为如果不满足断言,程序将简单地结束。

    注意此方法需要大量测试因为测试通常会在production/release建造!

  3. 函数可以returnNULL作为有效值IP and/or Port 正常运行下可能0

    重新设计函数以一种或另一种方式 return 一个单独的错误状态。

    这通常可以通过两种方式完成:

    • 使用函数的 return 值并通过作为参数传递的指针传回结果

      int InsertNode(unsigned int IP, unsigned short Port, struct Nodes** ppresult)
      {
        int error_state = 0;
      
        if (!IP || !Port || !ppresult)
        {
          errno = EINVAL; /* Setting errno, allows the caller to log 
                         the failure using the perror() function. */
          error_state = -1;
        }
        else
        {
          if (...)
          {
            *ppresult = &List[x];
          }
      
          ...
      
        }
      
        return error_state;
      }
      

      这样使用:

      struct Nodes * p;
      if (-1 == InsertNode (..., &p))
      {
         perror("InsertNode() failed");
        /* exit or error logging/handling */
      }
      
    • 通过作为参数传递的指针传回错误状态结果

      struct Nodes * InsertNode(unsigned int IP, unsigned short Port, int * perror_state)
      {
        int error_state = 0;
      
        if (!IP || !Port || !perror_state)
        {
          errno = EINVAL; /* Setting errno, allows the caller to log 
                         the failure using the perror() function. */
          error_state = -1;
        }
        else
        {
          if (...)
          {
            *ppresult = &List[x];
          }
      
          ...
      
        }
      
        *perror_state = error_state;
      
        return NULL;
      }
      

      这样使用:

      int result;
      struct Nodes * p = InsertNode (..., &result))
      if (-1 == result)
      {
        perror("InsertNode() failed");
        /* exit or error logging/handling */
      }
      

TLDR

"Until the switch from gcc 4.9 to 6.3 I didn't see a warning." 尝试使用 gcc -std=gnu90 进行编译,以便在与之前使用 gcc 4.9 时工作的条件类似的条件下进行编译。

好的,我在听

将编译器从 gcc 4.9 更改为 gcc 6.3 后看到编译器警告的原因是 gcc 4.9 默认为 C90(实际上是 C90 的 gnu90 方言),但到了 gcc 5.5,默认为 C11(实际上是 gnu11)。

C90 标准在 Constraints 部分中关于 return 声明的说明 (C90 §6.6.6.4):

A return statement with an expression shall not appear in a function whose return type is void.

但是 C11 标准中相同的 Constraints 部分说 C11 §6.8.6.4:

A return statement with an expression shall not appear in a function whose return type is void. A return statement without an expression shall only appear in a function whose return type is void.

现在,编译器必须为任何约束违规生成诊断消息 (§5.1.1.3)。当您的代码在 C90 下编译时没有违反约束,但更改为更新的编译器意味着代码现在在 C11 下编译,其中 约束违反,因此警告。

一个选项是简单地使用 gcc -std=gnu90 进行编译,允许使用您之前使用的相同 C 方言编译代码,即使在更新的编译器上也是如此。

但是,还要注意原始代码可能有 未定义的行为,因为 (C90 §6.6.6.4):

If a return statement with an expression is executed, and the value of the function call is used by the caller, the behavior is undefined.

如果调用者使用 InsertNode() 编辑的值 return,并且在函数调用中遇到 return; 语句,则您有未定义的行为。最好的选择是查看对 InsertNode() 的所有调用,看看它们如何处理 return 值。 return; 可能是一个拼写错误,并且代码已经处理了 returned 空指针,在这种情况下,更改为 return NULL; 将是修复代码所需的全部。如果代码尚未处理空指针, 用于修复代码。