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

问题描述

考虑以下代码:

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可能很有启发性-这可以帮助我们弄清楚为什么其他函数没有获得该属性。在我的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);
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...