简体   繁体   English

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

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

Consider this code:考虑这个代码:

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

int main(void)
{
    foo();
}

When compiled, it emits a warning:编译时,它会发出警告:

$ 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();
      |     ^~~~~

This is as expected.这正如预期的那样。 What I wonder is if there is any rationale why some many standard library functions is not declared with this attribute.我想知道为什么许多标准库函数没有用这个属性声明是否有任何理由。 I'm talking about functions like scanf where checking the return value is crucial in most cases, and also functions like malloc which is completely pointless if you do not use the return value.我说的是像scanf这样的函数,在大多数情况下,检查返回值是至关重要的,还有像malloc这样的函数,如果你不使用返回值,它是完全没有意义的。 However, realloc seems to have this.但是, realloc似乎有这个。

Is there any reason for not declaring scanf, malloc etc functions with __attribute__((warn_unused_result)) ?是否有任何理由不使用__attribute__((warn_unused_result))声明 scanf、malloc 等函数? I think this could have prevented many bugs.我认为这可以防止许多错误。

It may be enlightening to think about why these specific functions get warn_unused_result —this may help us figure out why other functions don't get the attribute.思考为什么这些特定函数会得到warn_unused_result可能会warn_unused_result ——这可能有助于我们弄清楚为什么其他函数没有得到该属性。 On my Glibc system, there are only two such functions:在我的 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));

On my macOS system, there are three:在我的 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);

So, why does this super-useful attribute only get used on realloc() and another near-identical function in Glibc?那么,为什么这个超级有用的属性只用于realloc()和 Glibc 中另一个几乎相同的函数?

The answer is because this attribute is designed to prevent a very specific bug.答案是因为此属性旨在防止非常具体的错误。 Let's say we have a 100-element array and want to resize it to add a 101st element (at index 100) (and let's ignore error handling):假设我们有一个 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;

Spot the bug?发现错误? This is a serious memory error but it is also a memory error that may or may not get noticed very quickly.这是一个严重的内存错误,但它也是一个内存错误,可能会也可能不会很快被注意到。 The original malloc will often round up to a larger size in the first place, and if realloc succeeds, you may end up with the same address anyway.最初的malloc通常会向上舍入到更大的大小,如果realloc成功,您最终可能会得到相同的地址。 The actual bug will only get noticed once you start writing into another object, or once another object gets allocated at the old address for arr .只有当您开始写入另一个对象,或者在arr的旧地址分配另一个对象时,才会注意到实际的错误。

This is something that might take you a while to debug.这可能需要您花一些时间来调试。

So, the warning is designed to catch this serious bug that is difficult to debug.因此,警告旨在捕获这个难以调试的严重错误。 Compare with the following:与以下内容进行比较:

malloc(100 * sizeof(int));

This bug isn't very serious at all—it's just a memory leak.这个错误根本不是很严重——它只是一个内存泄漏。 It's not even incorrect code!它甚至不是错误的代码!

As a fun exercise, try putting the above code into Godbolt and looking at the assembly output—GCC will actually remove the call to malloc entirely.作为一个有趣的练习,尝试将上述代码放入 Godbolt 并查看汇编输出——GCC 实际上会完全删除malloc的调用。

As for printf , scanf , and others—至于printfscanf等——

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

If GCC gave warnings for not checking the return of printf , people would get frustrated because they would see too many warnings in their code—and they would respond by turning the warnings off.如果 GCC 警告不检查printf的返回,人们会感到沮丧,因为他们会在他们的代码中看到太多警告——他们会通过关闭警告来做出回应。

So sometimes it is better to only trigger warnings for the most serious cases, and let some other cases slide.所以有时最好只针对最严重的情况触发警告,而让其他一些情况下滑。


As a minor technical note, this is somewhat more an issue for the standard library maintainers, like the Glibc team, rather than GCC or Clang.作为一个次要的技术说明,这对于标准库维护者来说更像是一个问题,比如 Glibc 团队,而不是 GCC 或 Clang。 Even though the standard library and compiler are tightly integrated, they are separate projects—and you can even swap out different standard libraries.尽管标准库和编译器紧密集成,但它们是独立的项目——您甚至可以交换不同的标准库。 For example, you might use musl, glibc, or Apple's libSystem.例如,您可以使用 musl、glibc 或 Apple 的 libSystem。

  1. It is not portable.它不便携。
  2. Standard does not require it.标准不需要它。
  3. You can always write the wrapper if you want be warned.如果您想被警告,您可以随时编写包装器。
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