警告不是编译时间常数的函数参数

时间:2019-08-26 15:49:21

标签: c gcc compiler-warnings

假设我正在维护一个使用两个参数(两个指针)的库函数。第二个参数仅是为了向后兼容而存在。调用者应始终传递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两者调用的警告。 (Compiler Explorer demo。)

任何人都可以建议一种替代结构,该结构将对warning_expected而不是warning_not_expected产生警告吗?

注意:

  • 奇怪的是,如果bint,则上面的does work
  • 以上使用GCC特定的扩展,但是,欢迎使用可在多种编译器上使用的解决方案。 (尤其是,clang并没有实现attribute((warning)),而且我也没有运气找到替代方法。)
  • 关闭优化后仍可使用的解决方案比不使用优化的解决方案更好。 (即使bint并且thefun被标记为始终内联,以上功能也无法关闭优化功能。)
  • 一种解决方案,其中不涉及将thefun定义为宏,而不是将其定义为宏。
  • 在C程序和C ++程序中包含标头时,标头必须起作用。适量的ifdage可接受。
  • 除非-Werror或同等功能处于活动状态,否则它必须是警告,而不是硬错误。

编辑:基于Kamil Cuk's discovery,可以通过将指针强制转换为具有不同大小的整数 来抑制不必要的警告,是对__builtin_constant_pfiled GCC bug report #91554实施的监督。我仍然对提供使用clang,icc或与GNU libc一起使用的任何其他编译器执行此操作的方式的答案感兴趣。

2 个答案:

答案 0 :(得分:4)

我终于设法使其正常工作:

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

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

似乎gcc无法对指针类型执行__builtin_constant_p__builtin_constant_p(b)始终返回0,因此warn函数始终处于链接状态。将b投射到int很奇怪。尽管它会降低指针值的精度,但我们对此并不在意,因为我们只检查它是否为常数。

答案 1 :(得分:3)

没有GNU扩展,就无法完成您描述的事情。

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

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

但是,在GCC和Clang上都有 one 个增强型方法:

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 bug report #91554