2

How do I solve the following throwing the warning in the title?

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

In other words, in the case of giving up through missing data, what should it return? Or do I need to trawl through the entire body of code and have checks every time to see if it should be called or not? The program functions as intended just returning at that point, if I'm to continue using it (or upgrade the OS it's running on), fixing compiler warnings seems like a good idea, they tend to turn into errors when compiler versions get bumped.

There's a clue in this answer which answers someone asking about the same warning, the answer doesn't give me quite enough info to proceed though, nor do the other's I've read.

Extra information: The check on the values of IP & Port are there to sanitize the content of &List, such cases indicate datagrams from misconfigured clients or traffic from persons with malicious intent, sad but it happens. It's invalid data we don't care about at all, logging it seems pointless, it shouldn't delay processing the next one, and absolutely not halt the program. Until the switch from gcc 4.9 to 6.3 I didn't see a warning. The current return; appears to simply black-hole it, but I only understand bits of the code's intent.

4

2 回答 2

2

在通过丢失数据放弃的情况下,它应该返回什么?

通常取决于。

有几种情况

  1. 该函数并非旨在NULL作为有效值返回。

    代替

    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. IP并且Port永远不会处于0正常运行状态。因此,如果它们是,那将是一个编程错误。

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

    所以而不是

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

    利用

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

    这里不需要特定的用法,因为如果不满足断言,程序将简单地结束。

    注意,这种方法需要大量测试,因为测试通常会在生产/发布版本中删除!

  3. 该函数可能NULL作为有效值返回 IP/或Port 可能处于0正常运行状态。

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

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

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

      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 */
      }
      
于 2018-08-12T08:41:27.227 回答
0

TLDR

“在从 gcc 4.9 切换到 6.3 之前,我没有看到警告。” 当您使用 gcc 4.9 时,尝试gcc -std=gnu90在与以前工作的条件类似的条件下进行编译。

好的,我在听

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

C90 标准在约束部分中return说(C90 §6.6.6.4):

带有表达式的return语句不应出现在返回类型为void的函数中。

但是来自 C11 标准的相同约束部分说C11 §6.8.6.4

带有表达式的return语句不应出现在返回类型为void的函数中。没有表达式return语句只能出现在返回类型为void的函数中。

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

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

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

如果执行带有表达式的return语句,并且调用者使用了函数调用的值,则行为未定义。

如果InsertNode()调用者使用了返回的值,并且return;在函数调用中遇到了该语句,则您有未定义的行为。最好的选择是查看所有调用以InsertNode()了解它们如何处理返回值。这可能return;是一个错字,并且代码已经处理了返回的空指针,在这种情况下,只需更改为return NULL;即可修复代码。如果代码还没有处理空指针,@alk 提供了几个修复代码的选项。

于 2018-08-12T14:22:17.023 回答