C语言 警告不是编译时常量的函数参数



假设我正在维护一个库函数,该函数接受两个参数,两个指针。 第二个参数仅用于向后兼容;调用方应始终传递 NULL。我想在我的头文件中放一些东西,如果第二个参数不是编译时常量 NULL,编译器会发出警告。 我想我可以使用 GCC 的__builtin_constant_p__attribute__((warning))扩展来做到这一点:

extern void thefun_called_with_nonnull_arg (void)
__attribute__((__warning__(
"'thefun' called with second argument not NULL")));
extern int real_thefun (void *, void *);
static inline int
thefun (void *a, void *b)
{
if (!__builtin_constant_p(b) || b != 0)
thefun_called_with_nonnull_arg();
return real_thefun(a, b);
}
int warning_expected (void *a, void *b)
{
return thefun(a, b);
}
int warning_not_expected (void *a)
{
return thefun(a, 0);
}

但这不适用于我测试过的任何版本的 GCC。 我收到两个呼叫thefun的警告。 (编译器资源管理器演示。

谁能提出一个替代结构,为warning_expected而不是warning_not_expected产生警告?

笔记:

  • 奇怪的是,如果b是一个int,上述方法确实有效。
  • 以上使用特定于 GCC 的扩展,但是欢迎适用于更广泛种类的编译器的解决方案。 (特别是,clang 没有实现attribute((warning)),我没有找到替代方案。
  • 关闭优化
  • 后仍然有效的解决方案比关闭优化的解决方案更可取。 (上述内容在关闭优化的情况下不起作用,即使bint并且thefun标记为始终内联。
  • 不涉及将thefun定义为宏的解决方案比将宏定义为宏的解决方案更可取。
  • 当从 C 程序和C++程序中包含标头时,标头必须有效。 适度的 ifdeffage 是可以接受的。
  • 它必须是警告,而不是硬错误,除非-Werror或等效项处于活动状态。

编辑:基于Kamil Cuk的发现,可以通过将指针投射到不同大小的整数来抑制不需要的警告,我已经确定这是实施__builtin_constant_p和提交的GCC错误报告#91554的疏忽。 我仍然对提供clang,icc或任何其他通常与GNU libc一起使用的编译器的方法的答案感兴趣。

我终于设法让它工作了:

if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {

这样,您只会收到一个警告。

似乎gcc无法对指针类型执行__builtin_constant_p__builtin_constant_p(b)始终返回 0,因此 warn 函数始终是链接的。将b选为int很奇怪。虽然它失去了指针值的精度,但我们并不关心它,因为我们只检查它是否是一个常量。

没有GNU扩展就没有办法做你所描述的。

这种可移植方法会产生硬错误(因为_Static_assert需要一个常量表达式):

#define thefun(a, b) 
({ 
_Static_assert(b == 0, 
"'thefun' called with second argument not NULL"); 
real_thefun(a, b); 
})

但是,有一种强化风格的方法适用于 GCC 和 Clang:

extern void thefun_called_with_nonnull_arg (void)
__attribute__((__deprecated__(
"'thefun' called with second argument not NULL")));
extern int real_thefun (void *, void *);
static inline int
thefun (void *a, void *b)
{
if (!__builtin_constant_p((unsigned short)(unsigned long)b) || b != 0)
thefun_called_with_nonnull_arg();
return real_thefun(a, b);
}
int warning_expected (void *a, void *b)
{
return thefun(a, b);
}
int warning_not_expected (void *a)
{
return thefun(a, 0);
}

使用 GCC 8.3.0 和 Clang 8.0.0 进行测试。

请参阅 GCC 错误报告 #91554 了解有关强制转换需求的更多信息。

相关内容

  • 没有找到相关文章

最新更新