gcc的-Wbad-function-cast选项的目的是什么?

6

根据这里的答案建议,我开启了-Wbad-function-cast来查看是否有不良行为gcc可以捕获,结果出现了以下示例:

unsigned long n;
// ...
int crossover = (int)pow(n, .14);

这里不是关键的地方,crossover 变量是一个 int 类型,但它也可以是 unsigned long 或其他类型,警告信息是一样的。

这似乎是一个很普通且有用的强制类型转换的例子,为什么会有问题?是否有理由保留这个警告呢?

我通常喜欢设置很多警告,但我无法理解这个警告的使用情况。我正在处理的代码涉及大量数字计算,经常需要将数据从一种类型转换为另一种类型以满足各种算法的需求。


我发现你可以用 +0.0f 来终止它。 - MSalters
2个回答

5

您最好认真对待这个警告。

如果你想从pow的浮点数结果中得到整数,那么这是一个舍入操作,必须使用其中一个标准舍入函数,如round。使用整数转换可能会导致意外情况:通常会失去小数部分,例如2.76可能会被截断为2,就像2.12一样。即使您想要这种行为,最好使用floor函数明确地指定。这将提高您代码的可读性和可维护性。


是的,我想要那种行为,这就是我为什么使用转换的原因。我注意到当我加入floor时代码会变得更慢;特别是在-O3时,我会得到roundsd然后是cvttsd2si,而不是简单的cvttsd2si。这对我有什么好处吗?(虽然这个应用程序受速度影响,但这个特定部分不在热循环中,所以我愿意付出性能损失,但只有在它能提供一些价值时才这样做。) - Charles
如果你 (1) 针对一个具体的架构并且 (2) 理解在这里截断转换的含义,那么你可以忽略或关闭此警告。它是在 C 中发出的,用于警告跨平台兼容性和舍入问题。另外,如果 pow 的双精度结果超过了最大整数值,则转换行为是未定义的,而 floor 行为总是被指定的。 - Konstantin Vladimirov
我同意溢出会导致未定义的行为,这是一件坏事。(这就是为什么我提供了我的示例代码--您可以看到在此处不可能发生溢出。)谢谢,我现在接受。 - Charles
10
推荐使用floor()函数可能会提高可读性,但实际上并没有帮助。floor()函数返回一个双精度数值,即使将其转换为(uint32_t),仍然会抛出-Wbad-function-cast的警告。 - lod
2
在C99及以上版本中,我们有lrint和llrint。本答案中的Floor仅用于确定舍入方向。即使没有任何rint,使用ceil或floor仍可能会收到警告。但我们不会感到惊讶。 - Konstantin Vladimirov
2
那么当 floortrunc 似乎无法解决问题时,该如何消除警告呢?此外,仅在直接从返回值进行转换时才会出现此警告(可以用于规避警告)- 那么警告的目的是什么呢?如果通过变量将 2.76 转换为 int,那么与直接输入是否一样糟糕呢? - skyking

2

-Wbad-function-cast警告的实用性有限。

很可能,-Wall-Wextra都没有启用该警告。此外,它不适用于C++(仅适用于C/Objective-C)。

您的具体示例既不利用未定义行为也不利用实现定义行为(参见ISO C11,第6.3.1.4节)。因此,此警告对您毫无好处。

相比之下,如果您试图重写代码以使-Wbad-function-cast满意,您只会添加多余的函数调用,即使使用-O3,最新的GCC/Clang编译器也无法优化掉
#include <math.h>
#include <fenv.h>
int f(unsigned n)
{
  int crossover = lrint(floor(pow(n, .14)));
  return crossover;
}

(负面示例,使用-Wbad-function-cast不发出no warning emitted但多余的函数调用)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接