如何在标题中解决以下问题?
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];
}
}
换句话说,在放弃缺失数据的情况下,应该返回什么?或者我是否需要遍历整个代码并每次检查是否应该调用它?如果我要继续使用该程序(或升级其运行的操作系统),该程序按预期运行,修复编译器警告似乎是一个好主意,当编译器版本受到冲击时,它们往往会变成错误。
在this answer有一个线索回答有人询问同样的警告,答案并没有给我足够的信息继续进行,也没有我读过的其他信息。
额外信息:检查IP和端口的值是为了清理&List的内容,这些情况表明来自配置错误的客户端的数据报或来自恶意目的的人的流量,可悲但是它发生了。这是我们根本不关心的无效数据,记录它似乎毫无意义,它不应该延迟处理下一个,并且绝对不会停止程序。直到从gcc 4.9切换到6.3我没有看到警告。目前的回报;看起来只是黑洞,但我只了解代码的意图。
在放弃缺失数据的情况下,它应该返回什么?
通常它取决于。
有几种情况
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 */
}
IP
和Port
永远不会是0
。所以,如果他们是一个编程错误。
在那些情况下,你可能没有不返回但结束程序。
而不是
if (!IP)
return;
if (!Port)
return;
使用
assert((IP) && (Port));
这里没有必要具体使用,因为如果不满足断言,程序就会结束。
请注意,此方法需要进行大量测试,因为测试通常会在生产/发布版本中删除!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 */
}
“直到从gcc 4.9切换到6.3我没有看到警告。”尝试使用gcc -std=gnu90
进行编译,在类似的条件下编译,使用gcc 4.9。
在将编译器从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 has provided several options用于修复代码。