繁体   English   中英

为什么 gcc 和 clang 不对库函数的未使用结果发出警告?

[英]Why does not gcc and clang warn for unused result of library functions?

考虑这个代码:

int __attribute__((warn_unused_result)) foo(void)
{
    return 42;
}

int main(void)
{
    foo();
}

编译时,它会发出警告:

$ gcc main.c
main.c: In function ‘main’:
main.c:8:5: warning: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     foo();
      |     ^~~~~

这正如预期的那样。 我想知道为什么许多标准库函数没有用这个属性声明是否有任何理由。 我说的是像scanf这样的函数,在大多数情况下,检查返回值是至关重要的,还有像malloc这样的函数,如果你不使用返回值,它是完全没有意义的。 但是, realloc似乎有这个。

是否有任何理由不使用__attribute__((warn_unused_result))声明 scanf、malloc 等函数? 我认为这可以防止许多错误。

思考为什么这些特定函数会得到warn_unused_result可能会warn_unused_result ——这可能有助于我们弄清楚为什么其他函数没有得到该属性。 在我的 Glibc 系统上,只有两个这样的函数:

extern void *realloc (void *__ptr, size_t __size)
     __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
     __THROW __attribute_warn_unused_result__
     __attribute_alloc_size__ ((2, 3));

在我的 macOS 系统上,一共有三个:

void    *malloc(size_t __size) __result_use_check __alloc_size(1);
void    *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
void    *realloc(void *__ptr, size_t __size) __result_use_check __alloc_size(2);

那么,为什么这个超级有用的属性只用于realloc()和 Glibc 中另一个几乎相同的函数?

答案是因为此属性旨在防止非常具体的错误。 假设我们有一个 100 个元素的数组,并且想要调整它的大小以添加第 101 个元素(在索引 100 处)(让我们忽略错误处理):

// Create 100-element array.
int *arr = malloc(sizeof(int) * 100);
arr[99] = 1234;
// Resize and add a 101st element.
realloc(arr, sizeof(int) * 101); // bug
arr[100] = 1234;

发现错误? 这是一个严重的内存错误,但它也是一个内存错误,可能会也可能不会很快被注意到。 最初的malloc通常会向上舍入到更大的大小,如果realloc成功,您最终可能会得到相同的地址。 只有当您开始写入另一个对象,或者在arr的旧地址分配另一个对象时,才会注意到实际的错误。

这可能需要您花一些时间来调试。

因此,警告旨在捕获这个难以调试的严重错误。 与以下内容进行比较:

malloc(100 * sizeof(int));

这个错误根本不是很严重——它只是一个内存泄漏。 它甚至不是错误的代码!

作为一个有趣的练习,尝试将上述代码放入 Godbolt 并查看汇编输出——GCC 实际上会完全删除malloc的调用。

至于printfscanf等——

int r = printf(...);
if (r == -1) {
    abort();
}

如果 GCC 警告不检查printf的返回,人们会感到沮丧,因为他们会在他们的代码中看到太多警告——他们会通过关闭警告来做出回应。

所以有时最好只针对最严重的情况触发警告,而让其他一些情况下滑。


作为一个次要的技术说明,这对于标准库维护者来说更像是一个问题,比如 Glibc 团队,而不是 GCC 或 Clang。 尽管标准库和编译器紧密集成,但它们是独立的项目——您甚至可以交换不同的标准库。 例如,您可以使用 musl、glibc 或 Apple 的 libSystem。

  1. 它不便携。
  2. 标准不需要它。
  3. 如果您想被警告,您可以随时编写包装器。
static inline void * __attribute__((warn_unused_result, always_inline)) mymalloc(size_t size)
{
    return malloc(size);
}

int main(void)
{
    mymalloc(300);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM